From 2b90ed0c43d1a49a6e678253ee872fb6ee0d0d04 Mon Sep 17 00:00:00 2001 From: Will Usher Date: Thu, 11 Nov 2021 09:07:51 -0800 Subject: [PATCH 01/26] Support 3D ISPC task launches in OSPRay's ISPC task system --- ospray/ispc_tasksys.cpp | 44 ++++++++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/ospray/ispc_tasksys.cpp b/ospray/ispc_tasksys.cpp index ec44991c67..6993fd7653 100644 --- a/ospray/ispc_tasksys.cpp +++ b/ospray/ispc_tasksys.cpp @@ -1,4 +1,4 @@ -// Copyright 2009-2020 Intel Corporation +// Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include @@ -10,8 +10,17 @@ namespace rkcommon { #define __dllexport /**/ /* Signature of ispc-generated 'task' functions */ -using ISPCTaskFunc = void (*)( - void *data, int threadIndex, int threadCount, int taskIndex, int taskCount); +using ISPCTaskFunc = void (*)(void *data, + int threadIndex, + int threadCount, + int taskIndex, + int taskCount, + int taskIndex0, + int taskIndex1, + int taskIndex2, + int taskCount0, + int taskCount1, + int taskCount2); extern "C" __dllexport void *ISPCAlloc( void **taskPtr, int64_t size, int32_t alignment) @@ -32,13 +41,30 @@ extern "C" __dllexport void ISPCSync(void *task) delete lst; } -extern "C" __dllexport void ISPCLaunch( - void ** /*taskPtr*/, void *func, void *data, int count) +extern "C" __dllexport void ISPCLaunch(void ** /*taskPtr*/, + void *func, + void *data, + int taskCount0, + int taskCount1, + int taskCount2) { - tasking::parallel_for(count, [&](const int i) { - const int threadIndex = i; //(int) TaskScheduler::threadIndex(); - const int threadCount = count; //(int) TaskScheduler::threadCount(); - ((ISPCTaskFunc)func)(data, threadIndex, threadCount, i, count); + const size_t nTasks = + size_t(taskCount0) * size_t(taskCount1) * size_t(taskCount2); + tasking::parallel_for(nTasks, [&](const size_t i) { + const int taskIndex0 = i % taskCount0; + const int taskIndex1 = (i / taskCount0) % taskCount1; + const int taskIndex2 = i / (taskCount0 * taskCount1); + ((ISPCTaskFunc)func)(data, + i, + nTasks, + i, + nTasks, + taskIndex0, + taskIndex1, + taskIndex2, + taskCount0, + taskCount1, + taskCount2); }); } } // namespace rkcommon From 7cb98c9310251b6acf440837843b40ea5a0c88d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20G=C3=BCnther?= Date: Fri, 26 Nov 2021 11:05:20 +0100 Subject: [PATCH 02/26] Bump version --- README.md | 10 +++++----- cmake/ospray_version.cmake | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 4aac50e5ed..46be3d289d 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ OSPRay ====== -This is release v2.8.0 of IntelĀ® OSPRay. For changes and new features -see the [changelog](CHANGELOG.md). Visit http://www.ospray.org for more -information. +This is release v2.9.0 (devel) of IntelĀ® OSPRay. For changes and new +features see the [changelog](CHANGELOG.md). Visit http://www.ospray.org +for more information. OSPRay Overview =============== @@ -351,9 +351,9 @@ Documentation ============= The following [API -documentation](http://www.sdvis.org/ospray/download/OSPRay_readme.pdf "OSPRay Documentation") +documentation](http://www.sdvis.org/ospray/download/OSPRay_readme_devel.pdf "OSPRay Documentation") of OSPRay can also be found as a [pdf -document](http://www.sdvis.org/ospray/download/OSPRay_readme.pdf "OSPRay Documentation"). +document](http://www.sdvis.org/ospray/download/OSPRay_readme_devel.pdf "OSPRay Documentation"). For a deeper explanation of the concepts, design, features and performance of OSPRay also have a look at the IEEE Vis 2016 paper diff --git a/cmake/ospray_version.cmake b/cmake/ospray_version.cmake index 1324feb790..0d0defcbfe 100644 --- a/cmake/ospray_version.cmake +++ b/cmake/ospray_version.cmake @@ -2,7 +2,7 @@ ## SPDX-License-Identifier: Apache-2.0 set(OSPRAY_VERSION_MAJOR 2) -set(OSPRAY_VERSION_MINOR 8) +set(OSPRAY_VERSION_MINOR 9) set(OSPRAY_VERSION_PATCH 0) set(OSPRAY_SOVERSION 2) set(OSPRAY_VERSION_GITHASH 0) From 49cecc70f373f67f9b93794188b57df3ce557280 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20G=C3=BCnther?= Date: Mon, 29 Nov 2021 10:26:20 +0100 Subject: [PATCH 03/26] Deformation motion blur for Mesh --- CHANGELOG.md | 5 + apps/ospTestSuite/test_light.cpp | 25 ++- apps/ospTestSuite/test_light.h | 3 +- apps/ospTestSuite/test_motionblur.cpp | 52 ++++++ doc/api.md | 19 ++- ospray/common/Data.h | 9 +- ospray/geometry/Geometry.h | 20 ++- ospray/geometry/Geometry.ih | 13 +- ospray/geometry/Mesh.cpp | 69 +++++++- ospray/geometry/Mesh.h | 8 +- ospray/geometry/Mesh.ispc | 150 +++++++++++++++--- ospray/geometry/Spheres.ispc | 3 +- ospray/render/pathtracer/GeometryLight.ispc | 5 +- ...cLight_LightMotionBlur_parameter_1.png.md5 | 1 + ...CameraRollingShutter_instance_mb_0.png.md5 | 2 +- ...CameraRollingShutter_instance_mb_1.png.md5 | 2 +- ...CameraRollingShutter_instance_mb_2.png.md5 | 2 +- ...CameraRollingShutter_instance_mb_3.png.md5 | 2 +- ...CameraRollingShutter_instance_mb_4.png.md5 | 2 +- ...CameraRollingShutter_instance_mb_5.png.md5 | 2 +- ...CameraRollingShutter_instance_mb_6.png.md5 | 2 +- ...CameraRollingShutter_instance_mb_7.png.md5 | 2 +- ...StereoRollingShutter_instance_mb_0.png.md5 | 2 +- ...Boxes_TestMotionBlur_instance_mb_0.png.md5 | 2 +- ...Boxes_TestMotionBlur_instance_mb_1.png.md5 | 2 +- ...cLight_LightMotionBlur_parameter_1.png.md5 | 1 + ...CameraRollingShutter_instance_mb_0.png.md5 | 2 +- ...CameraRollingShutter_instance_mb_1.png.md5 | 2 +- ...CameraRollingShutter_instance_mb_2.png.md5 | 2 +- ...CameraRollingShutter_instance_mb_3.png.md5 | 2 +- ...CameraRollingShutter_instance_mb_4.png.md5 | 2 +- ...CameraRollingShutter_instance_mb_5.png.md5 | 2 +- ...CameraRollingShutter_instance_mb_6.png.md5 | 2 +- ...CameraRollingShutter_instance_mb_7.png.md5 | 2 +- ...StereoRollingShutter_instance_mb_0.png.md5 | 2 +- ...Boxes_TestMotionBlur_instance_mb_0.png.md5 | 2 +- ...Boxes_TestMotionBlur_instance_mb_1.png.md5 | 2 +- 37 files changed, 351 insertions(+), 76 deletions(-) create mode 100644 test_image_data/baseline/AVX2/GeometricLight_LightMotionBlur_parameter_1.png.md5 create mode 100644 test_image_data/baseline/AVX512SKX/GeometricLight_LightMotionBlur_parameter_1.png.md5 diff --git a/CHANGELOG.md b/CHANGELOG.md index 28971d10e0..22072ef2d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ Version History --------------- +### Changes in v2.9.0: + +- Add support for multi-segment deformation motion blur for `mesh` + geometry + ### Changes in v2.8.0: - Lights can be now part of `OSPGroup` and thus instanced like diff --git a/apps/ospTestSuite/test_light.cpp b/apps/ospTestSuite/test_light.cpp index fa32fb9702..adba49fa29 100644 --- a/apps/ospTestSuite/test_light.cpp +++ b/apps/ospTestSuite/test_light.cpp @@ -121,8 +121,18 @@ void GeometricLight::SetUp() std::vector lightIndices = {{0, 1, 2, 3}}; cpp::Geometry lightMesh("mesh"); - lightMesh.setParam("vertex.position", cpp::CopiedData(lightVertices)); lightMesh.setParam("index", cpp::CopiedData(lightIndices)); + if (motionBlur == 2) { // deformation + for (vec3f &p : lightVertices) + p.x -= 0.5f; + std::vector mposdata; + mposdata.push_back(cpp::CopiedData(lightVertices)); + for (vec3f &p : lightVertices) + p.x += 1.0f; + mposdata.push_back(cpp::CopiedData(lightVertices)); + lightMesh.setParam("motion.vertex.position", cpp::CopiedData(mposdata)); + } else + lightMesh.setParam("vertex.position", cpp::CopiedData(lightVertices)); lightMesh.commit(); cpp::GeometricModel lightModel(lightMesh); @@ -140,7 +150,7 @@ void GeometricLight::SetUp() lightModel.setParam("material", lightMaterial); } - if (motionBlur) { + if (motionBlur == 1) { // instance lightModel.commit(); cpp::Group group; group.setParam("geometry", cpp::CopiedData(lightModel)); @@ -151,10 +161,11 @@ void GeometricLight::SetUp() xfms.push_back(affine3f::translate(vec3f(0.5, 0, 0))); instance.setParam("motion.transform", cpp::CopiedData(xfms)); AddInstance(instance); - - camera.setParam("shutter", range1f(0, 1)); } else AddModel(lightModel); + + if (motionBlur) + camera.setParam("shutter", range1f(0, 1)); } PhotometricLight::PhotometricLight() @@ -443,11 +454,13 @@ INSTANTIATE_TEST_SUITE_P(Light, GeometricLight, ::testing::Combine(::testing::Values(0.2f, 0.4f), ::testing::Bool(), - ::testing::Values(false))); + ::testing::Values(0))); INSTANTIATE_TEST_SUITE_P(LightMotionBlur, GeometricLight, - ::testing::Values(std::make_tuple(0.2f, false, true))); + ::testing::Combine(::testing::Values(0.2f), + ::testing::Values(false), + ::testing::Values(1, 2))); // Quad Light diff --git a/apps/ospTestSuite/test_light.h b/apps/ospTestSuite/test_light.h index 89bf9abe83..333d9baa51 100644 --- a/apps/ospTestSuite/test_light.h +++ b/apps/ospTestSuite/test_light.h @@ -57,7 +57,7 @@ class GeometricLight : public LightTest, public ::testing::TestWithParam> + int /*motionBlur 1=instance, 2=deformation*/>> { public: GeometricLight(); @@ -66,6 +66,7 @@ class GeometricLight private: float size{0.2}; bool useMaterialList{true}; + int motionBlur{0}; }; class PhotometricLight diff --git a/apps/ospTestSuite/test_motionblur.cpp b/apps/ospTestSuite/test_motionblur.cpp index 8451c3c395..70828c7f49 100644 --- a/apps/ospTestSuite/test_motionblur.cpp +++ b/apps/ospTestSuite/test_motionblur.cpp @@ -163,6 +163,58 @@ void MotionBlurBoxes::SetUp() AddInstance(instance); } + { // deformation + std::vector pos = {vec3f(-3.0f, -3.5f, -0.5f), + vec3f(-2.0f, -3.5f, -0.5f), + vec3f(-2.0f, -2.5f, -0.5f), + vec3f(-3.0f, -2.5f, -0.5f)}; + std::vector nor(4, vec3f(0.0f, 0.0f, -1.0f)); + std::vector mposdata; + mposdata.push_back(cpp::CopiedData(pos)); + std::vector mnordata; + mnordata.push_back(cpp::CopiedData(nor)); + for (vec3f &p : pos) + p.x -= 1.5f; + mposdata.push_back(cpp::CopiedData(pos)); + for (vec3f &n : nor) + n.x += 1.0f; + mnordata.push_back(cpp::CopiedData(nor)); + for (vec3f &p : pos) + p = xfmPoint(quatf::rotate(vec3f(0.4, 0, 1), -0.1), p); + mposdata.push_back(cpp::CopiedData(pos)); + for (vec3f &n : nor) + n.x -= 0.5f; + mnordata.push_back(cpp::CopiedData(nor)); + cpp::CopiedData mpos(mposdata); + cpp::CopiedData mnor(mnordata); + { // triangle + cpp::Geometry geom("mesh"); + geom.setParam("motion.vertex.position", mpos); + geom.setParam("motion.vertex.normal", mnor); + geom.setParam("index", cpp::CopiedData(vec3ui(0, 1, 2))); + geom.commit(); + cpp::GeometricModel model(geom); + model.setParam("material", material); + AddModel(model); + } + { // quad + cpp::Geometry geom("mesh"); + geom.setParam("motion.vertex.position", mpos); + geom.setParam("motion.vertex.normal", mnor); + geom.setParam("index", cpp::CopiedData(vec4ui(0, 1, 2, 3))); + geom.commit(); + cpp::GeometricModel model(geom); + model.setParam("material", material); + model.commit(); + cpp::Group group; + group.setParam("geometry", cpp::CopiedData(model)); + group.commit(); + cpp::Instance instance(group); + instance.setParam("transform", affine3f::translate(vec3f(0, 1.5, 0))); + AddInstance(instance); + } + } + cpp::Light distant("distant"); distant.setParam("intensity", 3.0f); distant.setParam("direction", vec3f(0.3f, -4.0f, 2.8f)); diff --git a/doc/api.md b/doc/api.md index fa96bac7c7..f2bdae46dc 100644 --- a/doc/api.md +++ b/doc/api.md @@ -1064,14 +1064,17 @@ A mesh consisting of either triangles or quads is created by calling `ospNewGeometry` with type string "`mesh`". Once created, a mesh recognizes the following parameters: - Type Name Description - -------------------- ---------------- ------------------------------------------------- - vec3f[] vertex.position [data] array of vertex positions - vec3f[] vertex.normal [data] array of vertex normals - vec4f[] / vec3f[] vertex.color [data] array of vertex colors (linear RGBA/RGB) - vec2f[] vertex.texcoord [data] array of vertex texture coordinates - vec3ui[] / vec4ui[] index [data] array of (either triangle or quad) indices (into the vertex array(s)) - -------------------- ---------------- ------------------------------------------------- + Type Name Description + -------------------- ----------------------- ------------------------------------------------- + vec3f[] vertex.position [data] array of vertex positions, overridden by `motion.*` arrays + vec3f[] vertex.normal [data] array of vertex normals, overridden by `motion.*` arrays + vec4f[] / vec3f[] vertex.color [data] array of vertex colors (linear RGBA/RGB) + vec2f[] vertex.texcoord [data] array of vertex texture coordinates + vec3ui[] / vec4ui[] index [data] array of (either triangle or quad) indices (into the vertex array(s)) + vec3f[][] motion.vertex.position [data] array of vertex position arrays (uniformly distributed keys for deformation motion blur) + vec3f[][] motion.vertex.normal [data] array of vertex normal arrays (uniformly distributed keys for deformation motion blur) + box1f time time associated with first and last key in `motion.*` arrays (for deformation motion blur), default [0, 1] + -------------------- ----------------------- ------------------------------------------------- : Parameters defining a mesh geometry. The data type of index arrays differentiates between the underlying diff --git a/ospray/common/Data.h b/ospray/common/Data.h index 9bbda4447e..31391171d7 100644 --- a/ospray/common/Data.h +++ b/ospray/common/Data.h @@ -217,6 +217,12 @@ struct DataT : public Data } }; +template +struct OSPTypeFor *> +{ + static constexpr OSPDataType value = OSP_DATA; +}; + // Inlined definitions ////////////////////////////////////////////////////// inline const ispc::Data1D *ispc(Ref &dataRef) @@ -260,7 +266,8 @@ template inline typename std::enable_if::value, bool>::type Data::is() const { - auto toType = OSPTypeFor::value; + auto toType = OSPTypeFor::type>::type *>::value; return (type == toType || (toType == OSP_OBJECT && isObjectType(type))) && dimensions <= DIM; // can iterate with higher dimensionality } diff --git a/ospray/geometry/Geometry.h b/ospray/geometry/Geometry.h index afa1b3eb92..0b1f21b41e 100644 --- a/ospray/geometry/Geometry.h +++ b/ospray/geometry/Geometry.h @@ -96,7 +96,7 @@ RTCFORMATFOR_SPECIALIZATION(affine2f, RTC_FORMAT_FLOAT2X3_COLUMN_MAJOR); RTCFORMATFOR_SPECIALIZATION(affine3f, RTC_FORMAT_FLOAT3X4_COLUMN_MAJOR); #undef RTCFORMATFOR_SPECIALIZATION -// ... via ospData, NoOp when data is invalid +// ... via Ref, NoOp when data is invalid template void setEmbreeGeometryBuffer(RTCGeometry geom, enum RTCBufferType type, @@ -114,6 +114,24 @@ void setEmbreeGeometryBuffer(RTCGeometry geom, dataRef->stride(), dataRef->size()); } +// ... via *ospData, NoOp when data is invalid +template +void setEmbreeGeometryBuffer(RTCGeometry geom, + enum RTCBufferType type, + const DataT *dataPtr, + unsigned int slot = 0) +{ + if (!dataPtr) + return; + rtcSetSharedGeometryBuffer(geom, + type, + slot, + RTCFormatFor::value, + dataPtr->data(), + 0, + dataPtr->stride(), + dataPtr->size()); +} // ... via an std::vector template void setEmbreeGeometryBuffer(RTCGeometry geom, diff --git a/ospray/geometry/Geometry.ih b/ospray/geometry/Geometry.ih index 375bcc8f12..5f257c38b7 100644 --- a/ospray/geometry/Geometry.ih +++ b/ospray/geometry/Geometry.ih @@ -48,16 +48,13 @@ typedef void (*Geometry_GetAreasFct)(const Geometry *const uniform, // sample the given primitive uniformly wrt. area typedef SampleAreaRes (*Geometry_SampleAreaFct)(const Geometry *const uniform, - const int32 primID //!< primitive to sample - , - const uniform affine3f &xfm //!< instance transformation (obj2world) - , - const uniform affine3f &rcp_xfm //!< inverse transformation (world2obj) - , - const vec2f &s //!< random numbers to generate the sample + const int32 primID, + const uniform affine3f &xfm, // instance transformation (obj2world) + const uniform affine3f &rcp_xfm, // inverse transformation (world2obj) + const vec2f &s, // random numbers to generate the sample + const float time // for deformation motion blur ); -/*! ispc-side equivalent of a geometry */ struct Geometry { /*! 'virtual' post-intersect function that fills in a diff --git a/ospray/geometry/Mesh.cpp b/ospray/geometry/Mesh.cpp index 21589a23b9..d0ced3a4ce 100644 --- a/ospray/geometry/Mesh.cpp +++ b/ospray/geometry/Mesh.cpp @@ -30,8 +30,51 @@ void Mesh::commit() throw std::runtime_error("invalid Embree device"); } - vertexData = getParamDataT("vertex.position", true); - normalData = getParamDataT("vertex.normal"); + motionVertexAddr.clear(); + motionNormalAddr.clear(); + + motionVertexData = + getParamDataT *>("motion.vertex.position"); + if (motionVertexData) { + motionBlur = motionVertexData->size() > 1; + // check types and strides + vertexData = (*motionVertexData)[0]; // use 1st key as fallback + const int64_t stride = vertexData->ispc.byteStride; + const size_t size = vertexData->ispc.numItems; + for (auto &&vtxData : *motionVertexData) { + if (vtxData->type != OSP_VEC3F || vtxData->ispc.numItems != size + || vtxData->ispc.byteStride != stride) + throw std::runtime_error( + "Mesh 'motion.vertex.position' arrays need to be" + " of same size and stride and have type vec3f"); + motionVertexAddr.push_back(vtxData->data()); + } + motionNormalData = + getParamDataT *>("motion.vertex.normal"); + if (motionNormalData) { + if (motionNormalData->size() < motionVertexData->size()) + throw std::runtime_error( + "Mesh 'motion.vertex.normal' array has less keys than" + " 'motion.vertex.position'"); + // check types and strides + normalData = (*motionNormalData)[0]; // use 1st key as fallback + const int64_t stride = normalData->ispc.byteStride; + for (auto &&norData : *motionNormalData) { + if (norData->type != OSP_VEC3F || norData->ispc.numItems != size + || norData->ispc.byteStride != stride) + throw std::runtime_error( + "Mesh 'motion.vertex.normal' arrays need to be" + " of same size and stride and have type vec3f"); + motionNormalAddr.push_back(norData->data()); + } + } else + normalData = nullptr; + } else { + motionBlur = false; + vertexData = getParamDataT("vertex.position", true); + normalData = getParamDataT("vertex.normal"); + } + colorData = getParam("vertex.color"); texcoordData = getParamDataT("vertex.texcoord"); indexData = getParamDataT("index"); @@ -42,7 +85,23 @@ void Mesh::commit() embreeGeometry = rtcNewGeometry(embreeDevice, isTri ? RTC_GEOMETRY_TYPE_TRIANGLE : RTC_GEOMETRY_TYPE_QUAD); - setEmbreeGeometryBuffer(embreeGeometry, RTC_BUFFER_TYPE_VERTEX, vertexData); + + time = range1f(0.0f, 1.0f); + if (motionBlur) { + time = getParam("time", range1f(0.0f, 1.0f)); + if (time.upper < time.lower) + time.upper = time.lower; + + rtcSetGeometryTimeStepCount(embreeGeometry, motionVertexData->size()); + size_t i = 0; + for (auto &&vtx : *motionVertexData) + setEmbreeGeometryBuffer(embreeGeometry, RTC_BUFFER_TYPE_VERTEX, vtx, i++); + rtcSetGeometryTimeRange(embreeGeometry, time.lower, time.upper); + } else { + rtcSetGeometryTimeStepCount(embreeGeometry, 1); + setEmbreeGeometryBuffer(embreeGeometry, RTC_BUFFER_TYPE_VERTEX, vertexData); + } + rtcSetSharedGeometryBuffer(embreeGeometry, RTC_BUFFER_TYPE_INDEX, 0, @@ -59,6 +118,10 @@ void Mesh::commit() ispc(normalData), ispc(colorData), ispc(texcoordData), + motionVertexAddr.data(), + motionNormalAddr.data(), + motionBlur ? motionVertexData->size() : 0, + (const ispc::box1f &)time, colorData && colorData->type == OSP_VEC4F, isTri); diff --git a/ospray/geometry/Mesh.h b/ospray/geometry/Mesh.h index e307a78e11..33c83d38ea 100644 --- a/ospray/geometry/Mesh.h +++ b/ospray/geometry/Mesh.h @@ -1,4 +1,4 @@ -// Copyright 2009-2020 Intel Corporation +// Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once @@ -25,6 +25,12 @@ struct OSPRAY_SDK_INTERFACE Mesh : public Geometry Ref colorData; Ref> texcoordData; Ref indexData; + bool motionBlur{false}; + Ref *>> motionVertexData; + std::vector motionVertexAddr; + Ref *>> motionNormalData; + std::vector motionNormalAddr; + range1f time{0.0f, 1.0f}; }; } // namespace ospray diff --git a/ospray/geometry/Mesh.ispc b/ospray/geometry/Mesh.ispc index 69d8e14613..0a06adb0ab 100644 --- a/ospray/geometry/Mesh.ispc +++ b/ospray/geometry/Mesh.ispc @@ -16,6 +16,10 @@ struct Mesh Data1D normal; Data1D color; Data1D texcoord; + uint8 **motionVertex; + uint8 **motionNormal; + uint32 motionKeys; + box1f time; int64 flagMask; // which attributes are missing and cannot be interpolated bool has_alpha; // 4th color component is valid bool is_triangleMesh; @@ -33,6 +37,30 @@ __define_quad_interpolate(vec2f); __define_quad_interpolate(vec3f); __define_quad_interpolate(vec4f); +inline float Mesh_calcKey(Mesh *uniform self, float time, int &idx) +{ + // should only get hits within time interval, just to be sure... + float t = clamp(time, self->time.lower, self->time.upper); + t = (t - self->time.lower) * rcp(box_size(self->time)) + * (self->motionKeys - 1); + idx = clamp((int)t, 0, self->motionKeys - 2); + t = clamp(t - idx); + return t; +} + +inline vec3f interpolate_vec3f(const uniform int64 byteStride, + uint8 *a0, + uint8 *a1, + const int index, + const float t) +{ + vec3f v0, v1; + int64 offs = byteStride * index; + v0 = *((const vec3f *)(a0 + offs)); + v1 = *((const vec3f *)(a1 + offs)); + return lerp(t, v0, v1); +} + static void QuadMesh_postIntersect(const Geometry *uniform _self, varying DifferentialGeometry &dg, const varying Ray &ray, @@ -59,11 +87,24 @@ static void QuadMesh_postIntersect(const Geometry *uniform _self, flags &= self->flagMask; if (flags & DG_NS) { - const vec3f a = get_vec3f(self->normal, index.x); - const vec3f b = get_vec3f(self->normal, index.y); - const vec3f c = get_vec3f(self->normal, index.z); - const vec3f d = get_vec3f(self->normal, index.w); - dg.Ns = quad_interpolate(uv, a, b, c, d); + if (self->motionKeys) { + int idx; + const float t = Mesh_calcKey(self, ray.time, idx); + const uniform int64 byteStride = self->normal.byteStride; + const uint8 *a0 = self->motionNormal[idx]; + const uint8 *a1 = self->motionNormal[idx + 1]; + const vec3f a = interpolate_vec3f(byteStride, a0, a1, index.x, t); + const vec3f b = interpolate_vec3f(byteStride, a0, a1, index.y, t); + const vec3f c = interpolate_vec3f(byteStride, a0, a1, index.z, t); + const vec3f d = interpolate_vec3f(byteStride, a0, a1, index.w, t); + dg.Ns = quad_interpolate(uv, a, b, c, d); + } else { + const vec3f a = get_vec3f(self->normal, index.x); + const vec3f b = get_vec3f(self->normal, index.y); + const vec3f c = get_vec3f(self->normal, index.z); + const vec3f d = get_vec3f(self->normal, index.w); + dg.Ns = quad_interpolate(uv, a, b, c, d); + } } if (flags & DG_COLOR) { @@ -93,9 +134,21 @@ static void QuadMesh_postIntersect(const Geometry *uniform _self, if (det != 0.f) { const float invDet = rcp(det); - const vec3f a = get_vec3f(self->vertex, index.x); - const vec3f b = get_vec3f(self->vertex, index.y); - const vec3f d = get_vec3f(self->vertex, index.w); + vec3f a, b, d; + if (self->motionKeys) { + int idx; + const float t = Mesh_calcKey(self, ray.time, idx); + const uniform int64 byteStride = self->vertex.byteStride; + const uint8 *a0 = self->motionVertex[idx]; + const uint8 *a1 = self->motionVertex[idx + 1]; + a = interpolate_vec3f(byteStride, a0, a1, index.x, t); + b = interpolate_vec3f(byteStride, a0, a1, index.y, t); + d = interpolate_vec3f(byteStride, a0, a1, index.w, t); + } else { + a = get_vec3f(self->vertex, index.x); + b = get_vec3f(self->vertex, index.y); + d = get_vec3f(self->vertex, index.w); + } const vec3f dp02 = a - d; const vec3f dp12 = b - d; dg.dPds = (dst12.y * dp02 - dst02.y * dp12) * invDet; @@ -124,10 +177,22 @@ static void TriangleMesh_PostIntersect(const Geometry *uniform _self, flags &= self->flagMask; if (flags & DG_NS) { - const vec3f a = get_vec3f(self->normal, index.x); - const vec3f b = get_vec3f(self->normal, index.y); - const vec3f c = get_vec3f(self->normal, index.z); - dg.Ns = interpolate(uv, a, b, c); + if (self->motionKeys) { + int idx; + const float t = Mesh_calcKey(self, ray.time, idx); + const uniform int64 byteStride = self->normal.byteStride; + const uint8 *a0 = self->motionNormal[idx]; + const uint8 *a1 = self->motionNormal[idx + 1]; + const vec3f a = interpolate_vec3f(byteStride, a0, a1, index.x, t); + const vec3f b = interpolate_vec3f(byteStride, a0, a1, index.y, t); + const vec3f c = interpolate_vec3f(byteStride, a0, a1, index.z, t); + dg.Ns = interpolate(uv, a, b, c); + } else { + const vec3f a = get_vec3f(self->normal, index.x); + const vec3f b = get_vec3f(self->normal, index.y); + const vec3f c = get_vec3f(self->normal, index.z); + dg.Ns = interpolate(uv, a, b, c); + } } if (flags & DG_COLOR) { @@ -154,9 +219,21 @@ static void TriangleMesh_PostIntersect(const Geometry *uniform _self, if (det != 0.f) { const float invDet = rcp(det); - const vec3f a = get_vec3f(self->vertex, index.x); - const vec3f b = get_vec3f(self->vertex, index.y); - const vec3f c = get_vec3f(self->vertex, index.z); + vec3f a, b, c; + if (self->motionKeys) { + int idx; + const float t = Mesh_calcKey(self, ray.time, idx); + const uniform int64 byteStride = self->vertex.byteStride; + const uint8 *a0 = self->motionVertex[idx]; + const uint8 *a1 = self->motionVertex[idx + 1]; + a = interpolate_vec3f(byteStride, a0, a1, index.x, t); + b = interpolate_vec3f(byteStride, a0, a1, index.y, t); + c = interpolate_vec3f(byteStride, a0, a1, index.z, t); + } else { + a = get_vec3f(self->vertex, index.x); + b = get_vec3f(self->vertex, index.y); + c = get_vec3f(self->vertex, index.z); + } const vec3f dp02 = a - c; const vec3f dp12 = b - c; dg.dPds = (dst12.y * dp02 - dst02.y * dp12) * invDet; @@ -176,21 +253,41 @@ SampleAreaRes Mesh_sampleArea(const Geometry *uniform const _self, const int32 primID, const uniform affine3f &xfm, const uniform affine3f &, - const vec2f &s) + const vec2f &s, + const float time) { const Mesh *const uniform self = (const Mesh *uniform)_self; SampleAreaRes res; vec4ui index4; + bool quad = false; if (self->is_triangleMesh) { const vec3ui index3 = get_vec3ui(self->index, primID); index4 = make_vec4ui(index3.x, index3.y, index3.z, index3.z); - } else + } else { index4 = get_vec4ui(self->index, primID); + quad = index4.z != index4.w; + } const vec4ui index = index4; - const vec3f a = get_vec3f(self->vertex, index.x); - const vec3f b = get_vec3f(self->vertex, index.y); - const vec3f c = get_vec3f(self->vertex, index.z); + vec3f a, b, c, d; + if (self->motionKeys) { + int idx; + const float t = Mesh_calcKey(self, time, idx); + const uniform int64 byteStride = self->vertex.byteStride; + const uint8 *a0 = self->motionVertex[idx]; + const uint8 *a1 = self->motionVertex[idx + 1]; + a = interpolate_vec3f(byteStride, a0, a1, index.x, t); + b = interpolate_vec3f(byteStride, a0, a1, index.y, t); + c = interpolate_vec3f(byteStride, a0, a1, index.z, t); + if (quad) + d = interpolate_vec3f(byteStride, a0, a1, index.w, t); + } else { + a = get_vec3f(self->vertex, index.x); + b = get_vec3f(self->vertex, index.y); + c = get_vec3f(self->vertex, index.z); + if (quad) + d = get_vec3f(self->vertex, index.w); + } const vec3f e1 = xfmVector(xfm, a - c); const vec3f e2 = xfmVector(xfm, b - c); const vec3f m1 = cross(e1, e2); @@ -201,8 +298,7 @@ SampleAreaRes Mesh_sampleArea(const Geometry *uniform const _self, vec3f v2 = c; vec2f sp = s; - if (index.z != index.w) { - const vec3f d = get_vec3f(self->vertex, index.w); + if (quad) { // painfully slow: re-calculate areas to decide which triangle to sample const vec3f e3 = xfmVector(xfm, d - c); const vec3f m2 = cross(e2, e3); @@ -235,6 +331,8 @@ void Mesh_getAreas(const Geometry *const uniform _self, { const Mesh *const uniform self = (const Mesh *uniform)_self; // TODO vectorize this loop, with foreach or ProgramCount & ProgramIndex + // XXX for deformation motion blur the areas (i.e. selection probabilities) + // are calculated using the first key only for (uniform int32 i = 0; i < numPrims; i++) { const uniform vec4ui index = self->is_triangleMesh ? make_vec4ui(get_vec3ui(self->index, primIDs[i])) @@ -278,6 +376,10 @@ export void *uniform Mesh_set(void *uniform _self, const Data1D *uniform normal, const Data1D *uniform color, const Data1D *uniform texcoord, + void *uniform motionVertex, + void *uniform motionNormal, + uniform unsigned int motionKeys, + const uniform box1f &time, uniform bool has_alpha, uniform bool is_triangleMesh) { @@ -287,6 +389,10 @@ export void *uniform Mesh_set(void *uniform _self, self->normal = *normal; self->color = *color; self->texcoord = *texcoord; + self->motionVertex = (uint8 * *uniform) motionVertex; + self->motionNormal = (uint8 * *uniform) motionNormal; + self->motionKeys = motionKeys; + self->time = time; self->has_alpha = has_alpha; self->super.numPrimitives = index->numItems; self->is_triangleMesh = is_triangleMesh; diff --git a/ospray/geometry/Spheres.ispc b/ospray/geometry/Spheres.ispc index 06130d1ab0..91ea3e784a 100644 --- a/ospray/geometry/Spheres.ispc +++ b/ospray/geometry/Spheres.ispc @@ -95,7 +95,8 @@ SampleAreaRes Spheres_sampleArea(const Geometry *uniform const _self, const int32 primID, const uniform affine3f &xfm, const uniform affine3f &rcp_xfm, - const vec2f &s) + const vec2f &s, + const float) { const Spheres *const uniform self = (const Spheres *uniform)_self; diff --git a/ospray/render/pathtracer/GeometryLight.ispc b/ospray/render/pathtracer/GeometryLight.ispc index cdf8125efa..e4a4a37123 100644 --- a/ospray/render/pathtracer/GeometryLight.ispc +++ b/ospray/render/pathtracer/GeometryLight.ispc @@ -84,10 +84,11 @@ Light_SampleRes GeometryLight_sample(const Light *uniform super, if (instance->motionBlur) foreach_unique (utime in time) { const uniform affine3f xfm = Instance_getTransform(instance, utime); - as = geo->sampleArea(geo, primID, xfm, rcp(xfm), ns); + as = geo->sampleArea(geo, primID, xfm, rcp(xfm), ns, time); } else { - as = geo->sampleArea(geo, primID, instance->xfm, instance->rcp_xfm, ns); + as = geo->sampleArea( + geo, primID, instance->xfm, instance->rcp_xfm, ns, time); } // note that sample.pdf/primitives * sampleArea(worldspace).pdf == self->pdf diff --git a/test_image_data/baseline/AVX2/GeometricLight_LightMotionBlur_parameter_1.png.md5 b/test_image_data/baseline/AVX2/GeometricLight_LightMotionBlur_parameter_1.png.md5 new file mode 100644 index 0000000000..c15e554e3d --- /dev/null +++ b/test_image_data/baseline/AVX2/GeometricLight_LightMotionBlur_parameter_1.png.md5 @@ -0,0 +1 @@ +d890634b6f26ef2c69b8201c300785ec diff --git a/test_image_data/baseline/AVX2/MotionBlurBoxes_CameraRollingShutter_instance_mb_0.png.md5 b/test_image_data/baseline/AVX2/MotionBlurBoxes_CameraRollingShutter_instance_mb_0.png.md5 index 876da5b964..5d6d7e7b78 100644 --- a/test_image_data/baseline/AVX2/MotionBlurBoxes_CameraRollingShutter_instance_mb_0.png.md5 +++ b/test_image_data/baseline/AVX2/MotionBlurBoxes_CameraRollingShutter_instance_mb_0.png.md5 @@ -1 +1 @@ -2631dd5eee3fd10a7ed5b72bfe9d7d1a +e09a7f947946e09944af1c0ee8eb3225 diff --git a/test_image_data/baseline/AVX2/MotionBlurBoxes_CameraRollingShutter_instance_mb_1.png.md5 b/test_image_data/baseline/AVX2/MotionBlurBoxes_CameraRollingShutter_instance_mb_1.png.md5 index 4ef5745194..200d17e22e 100644 --- a/test_image_data/baseline/AVX2/MotionBlurBoxes_CameraRollingShutter_instance_mb_1.png.md5 +++ b/test_image_data/baseline/AVX2/MotionBlurBoxes_CameraRollingShutter_instance_mb_1.png.md5 @@ -1 +1 @@ -d009fd188ab28d94f64be84c77ef13ed +84a327f24353d9c16b1f57cdf8738c52 diff --git a/test_image_data/baseline/AVX2/MotionBlurBoxes_CameraRollingShutter_instance_mb_2.png.md5 b/test_image_data/baseline/AVX2/MotionBlurBoxes_CameraRollingShutter_instance_mb_2.png.md5 index 9df5acfa4b..1da0937030 100644 --- a/test_image_data/baseline/AVX2/MotionBlurBoxes_CameraRollingShutter_instance_mb_2.png.md5 +++ b/test_image_data/baseline/AVX2/MotionBlurBoxes_CameraRollingShutter_instance_mb_2.png.md5 @@ -1 +1 @@ -fba6bbfd4defc357cfc4a4948b4f1d17 +c1a6af91c50b68c06f8d97b4316f1279 diff --git a/test_image_data/baseline/AVX2/MotionBlurBoxes_CameraRollingShutter_instance_mb_3.png.md5 b/test_image_data/baseline/AVX2/MotionBlurBoxes_CameraRollingShutter_instance_mb_3.png.md5 index e2bcf24021..367ceea8ca 100644 --- a/test_image_data/baseline/AVX2/MotionBlurBoxes_CameraRollingShutter_instance_mb_3.png.md5 +++ b/test_image_data/baseline/AVX2/MotionBlurBoxes_CameraRollingShutter_instance_mb_3.png.md5 @@ -1 +1 @@ -ff0f3a918ddcd3dfbfd34938b8d2398f +24b27522f08c8d46b384b0c3ac834133 diff --git a/test_image_data/baseline/AVX2/MotionBlurBoxes_CameraRollingShutter_instance_mb_4.png.md5 b/test_image_data/baseline/AVX2/MotionBlurBoxes_CameraRollingShutter_instance_mb_4.png.md5 index f4af5c9d1a..9974411221 100644 --- a/test_image_data/baseline/AVX2/MotionBlurBoxes_CameraRollingShutter_instance_mb_4.png.md5 +++ b/test_image_data/baseline/AVX2/MotionBlurBoxes_CameraRollingShutter_instance_mb_4.png.md5 @@ -1 +1 @@ -c54c3343f640605153ec4a890226cbaa +7f1ca47683d8f2a8398e66e2a9a78dde diff --git a/test_image_data/baseline/AVX2/MotionBlurBoxes_CameraRollingShutter_instance_mb_5.png.md5 b/test_image_data/baseline/AVX2/MotionBlurBoxes_CameraRollingShutter_instance_mb_5.png.md5 index ecfbbbf642..084fbb4fc1 100644 --- a/test_image_data/baseline/AVX2/MotionBlurBoxes_CameraRollingShutter_instance_mb_5.png.md5 +++ b/test_image_data/baseline/AVX2/MotionBlurBoxes_CameraRollingShutter_instance_mb_5.png.md5 @@ -1 +1 @@ -3a94abf020f55155240075b49f79d1fc +4e17234519beab039e833d481d74cf1f diff --git a/test_image_data/baseline/AVX2/MotionBlurBoxes_CameraRollingShutter_instance_mb_6.png.md5 b/test_image_data/baseline/AVX2/MotionBlurBoxes_CameraRollingShutter_instance_mb_6.png.md5 index 2ea26b3f93..0520f0c772 100644 --- a/test_image_data/baseline/AVX2/MotionBlurBoxes_CameraRollingShutter_instance_mb_6.png.md5 +++ b/test_image_data/baseline/AVX2/MotionBlurBoxes_CameraRollingShutter_instance_mb_6.png.md5 @@ -1 +1 @@ -8ae940d22fcb9d1ad333f613ff460610 +abe391f40c73fd61fee52307bbb1ff6c diff --git a/test_image_data/baseline/AVX2/MotionBlurBoxes_CameraRollingShutter_instance_mb_7.png.md5 b/test_image_data/baseline/AVX2/MotionBlurBoxes_CameraRollingShutter_instance_mb_7.png.md5 index 154226e606..5080f1db97 100644 --- a/test_image_data/baseline/AVX2/MotionBlurBoxes_CameraRollingShutter_instance_mb_7.png.md5 +++ b/test_image_data/baseline/AVX2/MotionBlurBoxes_CameraRollingShutter_instance_mb_7.png.md5 @@ -1 +1 @@ -821be901100abdfc3fae0b9dde745824 +bd05dcc671fe72f741659d8438901ac6 diff --git a/test_image_data/baseline/AVX2/MotionBlurBoxes_CameraStereoRollingShutter_instance_mb_0.png.md5 b/test_image_data/baseline/AVX2/MotionBlurBoxes_CameraStereoRollingShutter_instance_mb_0.png.md5 index d493ebe3b6..bbbd523fa2 100644 --- a/test_image_data/baseline/AVX2/MotionBlurBoxes_CameraStereoRollingShutter_instance_mb_0.png.md5 +++ b/test_image_data/baseline/AVX2/MotionBlurBoxes_CameraStereoRollingShutter_instance_mb_0.png.md5 @@ -1 +1 @@ -23f1bfaa226bc123270d63ea041a94f3 +4f9858dff7a78b5ae508fcaf272d3795 diff --git a/test_image_data/baseline/AVX2/MotionBlurBoxes_TestMotionBlur_instance_mb_0.png.md5 b/test_image_data/baseline/AVX2/MotionBlurBoxes_TestMotionBlur_instance_mb_0.png.md5 index 3ad59da9e6..31c999a3b5 100644 --- a/test_image_data/baseline/AVX2/MotionBlurBoxes_TestMotionBlur_instance_mb_0.png.md5 +++ b/test_image_data/baseline/AVX2/MotionBlurBoxes_TestMotionBlur_instance_mb_0.png.md5 @@ -1 +1 @@ -74c6671a307f4994d2798f3cae4929a4 +20fdf05b9a77272b90133e3f2e177dce diff --git a/test_image_data/baseline/AVX2/MotionBlurBoxes_TestMotionBlur_instance_mb_1.png.md5 b/test_image_data/baseline/AVX2/MotionBlurBoxes_TestMotionBlur_instance_mb_1.png.md5 index b99fb5abf6..566ab2cc39 100644 --- a/test_image_data/baseline/AVX2/MotionBlurBoxes_TestMotionBlur_instance_mb_1.png.md5 +++ b/test_image_data/baseline/AVX2/MotionBlurBoxes_TestMotionBlur_instance_mb_1.png.md5 @@ -1 +1 @@ -ec385b39810c7bdfd9bdf3bf6909fdb8 +1d093c1334f74362931d235a587b86c2 diff --git a/test_image_data/baseline/AVX512SKX/GeometricLight_LightMotionBlur_parameter_1.png.md5 b/test_image_data/baseline/AVX512SKX/GeometricLight_LightMotionBlur_parameter_1.png.md5 new file mode 100644 index 0000000000..c15e554e3d --- /dev/null +++ b/test_image_data/baseline/AVX512SKX/GeometricLight_LightMotionBlur_parameter_1.png.md5 @@ -0,0 +1 @@ +d890634b6f26ef2c69b8201c300785ec diff --git a/test_image_data/baseline/AVX512SKX/MotionBlurBoxes_CameraRollingShutter_instance_mb_0.png.md5 b/test_image_data/baseline/AVX512SKX/MotionBlurBoxes_CameraRollingShutter_instance_mb_0.png.md5 index 876da5b964..5d6d7e7b78 100644 --- a/test_image_data/baseline/AVX512SKX/MotionBlurBoxes_CameraRollingShutter_instance_mb_0.png.md5 +++ b/test_image_data/baseline/AVX512SKX/MotionBlurBoxes_CameraRollingShutter_instance_mb_0.png.md5 @@ -1 +1 @@ -2631dd5eee3fd10a7ed5b72bfe9d7d1a +e09a7f947946e09944af1c0ee8eb3225 diff --git a/test_image_data/baseline/AVX512SKX/MotionBlurBoxes_CameraRollingShutter_instance_mb_1.png.md5 b/test_image_data/baseline/AVX512SKX/MotionBlurBoxes_CameraRollingShutter_instance_mb_1.png.md5 index 4ef5745194..200d17e22e 100644 --- a/test_image_data/baseline/AVX512SKX/MotionBlurBoxes_CameraRollingShutter_instance_mb_1.png.md5 +++ b/test_image_data/baseline/AVX512SKX/MotionBlurBoxes_CameraRollingShutter_instance_mb_1.png.md5 @@ -1 +1 @@ -d009fd188ab28d94f64be84c77ef13ed +84a327f24353d9c16b1f57cdf8738c52 diff --git a/test_image_data/baseline/AVX512SKX/MotionBlurBoxes_CameraRollingShutter_instance_mb_2.png.md5 b/test_image_data/baseline/AVX512SKX/MotionBlurBoxes_CameraRollingShutter_instance_mb_2.png.md5 index 9df5acfa4b..1da0937030 100644 --- a/test_image_data/baseline/AVX512SKX/MotionBlurBoxes_CameraRollingShutter_instance_mb_2.png.md5 +++ b/test_image_data/baseline/AVX512SKX/MotionBlurBoxes_CameraRollingShutter_instance_mb_2.png.md5 @@ -1 +1 @@ -fba6bbfd4defc357cfc4a4948b4f1d17 +c1a6af91c50b68c06f8d97b4316f1279 diff --git a/test_image_data/baseline/AVX512SKX/MotionBlurBoxes_CameraRollingShutter_instance_mb_3.png.md5 b/test_image_data/baseline/AVX512SKX/MotionBlurBoxes_CameraRollingShutter_instance_mb_3.png.md5 index e2bcf24021..367ceea8ca 100644 --- a/test_image_data/baseline/AVX512SKX/MotionBlurBoxes_CameraRollingShutter_instance_mb_3.png.md5 +++ b/test_image_data/baseline/AVX512SKX/MotionBlurBoxes_CameraRollingShutter_instance_mb_3.png.md5 @@ -1 +1 @@ -ff0f3a918ddcd3dfbfd34938b8d2398f +24b27522f08c8d46b384b0c3ac834133 diff --git a/test_image_data/baseline/AVX512SKX/MotionBlurBoxes_CameraRollingShutter_instance_mb_4.png.md5 b/test_image_data/baseline/AVX512SKX/MotionBlurBoxes_CameraRollingShutter_instance_mb_4.png.md5 index f4af5c9d1a..9974411221 100644 --- a/test_image_data/baseline/AVX512SKX/MotionBlurBoxes_CameraRollingShutter_instance_mb_4.png.md5 +++ b/test_image_data/baseline/AVX512SKX/MotionBlurBoxes_CameraRollingShutter_instance_mb_4.png.md5 @@ -1 +1 @@ -c54c3343f640605153ec4a890226cbaa +7f1ca47683d8f2a8398e66e2a9a78dde diff --git a/test_image_data/baseline/AVX512SKX/MotionBlurBoxes_CameraRollingShutter_instance_mb_5.png.md5 b/test_image_data/baseline/AVX512SKX/MotionBlurBoxes_CameraRollingShutter_instance_mb_5.png.md5 index ecfbbbf642..084fbb4fc1 100644 --- a/test_image_data/baseline/AVX512SKX/MotionBlurBoxes_CameraRollingShutter_instance_mb_5.png.md5 +++ b/test_image_data/baseline/AVX512SKX/MotionBlurBoxes_CameraRollingShutter_instance_mb_5.png.md5 @@ -1 +1 @@ -3a94abf020f55155240075b49f79d1fc +4e17234519beab039e833d481d74cf1f diff --git a/test_image_data/baseline/AVX512SKX/MotionBlurBoxes_CameraRollingShutter_instance_mb_6.png.md5 b/test_image_data/baseline/AVX512SKX/MotionBlurBoxes_CameraRollingShutter_instance_mb_6.png.md5 index 2ea26b3f93..0520f0c772 100644 --- a/test_image_data/baseline/AVX512SKX/MotionBlurBoxes_CameraRollingShutter_instance_mb_6.png.md5 +++ b/test_image_data/baseline/AVX512SKX/MotionBlurBoxes_CameraRollingShutter_instance_mb_6.png.md5 @@ -1 +1 @@ -8ae940d22fcb9d1ad333f613ff460610 +abe391f40c73fd61fee52307bbb1ff6c diff --git a/test_image_data/baseline/AVX512SKX/MotionBlurBoxes_CameraRollingShutter_instance_mb_7.png.md5 b/test_image_data/baseline/AVX512SKX/MotionBlurBoxes_CameraRollingShutter_instance_mb_7.png.md5 index 154226e606..5080f1db97 100644 --- a/test_image_data/baseline/AVX512SKX/MotionBlurBoxes_CameraRollingShutter_instance_mb_7.png.md5 +++ b/test_image_data/baseline/AVX512SKX/MotionBlurBoxes_CameraRollingShutter_instance_mb_7.png.md5 @@ -1 +1 @@ -821be901100abdfc3fae0b9dde745824 +bd05dcc671fe72f741659d8438901ac6 diff --git a/test_image_data/baseline/AVX512SKX/MotionBlurBoxes_CameraStereoRollingShutter_instance_mb_0.png.md5 b/test_image_data/baseline/AVX512SKX/MotionBlurBoxes_CameraStereoRollingShutter_instance_mb_0.png.md5 index d493ebe3b6..bbbd523fa2 100644 --- a/test_image_data/baseline/AVX512SKX/MotionBlurBoxes_CameraStereoRollingShutter_instance_mb_0.png.md5 +++ b/test_image_data/baseline/AVX512SKX/MotionBlurBoxes_CameraStereoRollingShutter_instance_mb_0.png.md5 @@ -1 +1 @@ -23f1bfaa226bc123270d63ea041a94f3 +4f9858dff7a78b5ae508fcaf272d3795 diff --git a/test_image_data/baseline/AVX512SKX/MotionBlurBoxes_TestMotionBlur_instance_mb_0.png.md5 b/test_image_data/baseline/AVX512SKX/MotionBlurBoxes_TestMotionBlur_instance_mb_0.png.md5 index 3ad59da9e6..31c999a3b5 100644 --- a/test_image_data/baseline/AVX512SKX/MotionBlurBoxes_TestMotionBlur_instance_mb_0.png.md5 +++ b/test_image_data/baseline/AVX512SKX/MotionBlurBoxes_TestMotionBlur_instance_mb_0.png.md5 @@ -1 +1 @@ -74c6671a307f4994d2798f3cae4929a4 +20fdf05b9a77272b90133e3f2e177dce diff --git a/test_image_data/baseline/AVX512SKX/MotionBlurBoxes_TestMotionBlur_instance_mb_1.png.md5 b/test_image_data/baseline/AVX512SKX/MotionBlurBoxes_TestMotionBlur_instance_mb_1.png.md5 index b99fb5abf6..566ab2cc39 100644 --- a/test_image_data/baseline/AVX512SKX/MotionBlurBoxes_TestMotionBlur_instance_mb_1.png.md5 +++ b/test_image_data/baseline/AVX512SKX/MotionBlurBoxes_TestMotionBlur_instance_mb_1.png.md5 @@ -1 +1 @@ -ec385b39810c7bdfd9bdf3bf6909fdb8 +1d093c1334f74362931d235a587b86c2 From 733807c0763290d4933cd99a8c401150258bf1b6 Mon Sep 17 00:00:00 2001 From: Trevor Thomson Date: Thu, 2 Dec 2021 09:36:14 +0000 Subject: [PATCH 04/26] Migrate files for CPU module Define global variables for categories of apps Fix inconsistent default device in C99 header --- .gitignore | 5 +- CHANGELOG.md | 7 + CMakeLists.txt | 8 +- README.md | 6 +- apps/CMakeLists.txt | 36 +- apps/common/CMakeLists.txt | 6 +- apps/common/external/CMakeLists.txt | 2 +- apps/ospBenchmark/CMakeLists.txt | 2 +- apps/ospExamples/CMakeLists.txt | 4 +- apps/ospTestSuite/CMakeLists.txt | 4 +- apps/ospTutorial/CMakeLists.txt | 4 +- apps/utility/CMakeLists.txt | 4 +- .../ospray_cmake_config/osprayConfig.cmake.in | 4 +- cmake/ospray_macros.cmake | 12 +- cmake/ospray_options.cmake | 33 ++ doc/compilation.md | 4 +- doc/tutorials.md | 2 +- modules/CMakeLists.txt | 13 +- modules/cpu/CMakeLists.txt | 328 ++++++++++++++++++ {ospray/api => modules/cpu}/ISPCDevice.cpp | 4 +- {ospray/api => modules/cpu}/ISPCDevice.h | 0 {ospray/api => modules/cpu}/ISPCDevice.ispc | 0 {ospray => modules/cpu}/camera/Camera.cpp | 0 {ospray => modules/cpu}/camera/Camera.h | 0 {ospray => modules/cpu}/camera/Camera.ih | 0 {ospray => modules/cpu}/camera/Camera.ispc | 0 .../cpu}/camera/OrthographicCamera.cpp | 0 .../cpu}/camera/OrthographicCamera.h | 0 .../cpu}/camera/OrthographicCamera.ispc | 0 .../cpu}/camera/PanoramicCamera.cpp | 0 .../cpu}/camera/PanoramicCamera.h | 0 .../cpu}/camera/PanoramicCamera.ispc | 0 .../cpu}/camera/PerspectiveCamera.cpp | 0 .../cpu}/camera/PerspectiveCamera.h | 0 .../cpu}/camera/PerspectiveCamera.ispc | 0 .../cpu}/camera/registration.cpp | 0 {ospray => modules/cpu}/camera/registration.h | 0 {ospray => modules/cpu}/common/Clipping.ih | 0 {ospray => modules/cpu}/common/Data.cpp | 0 {ospray => modules/cpu}/common/Data.h | 0 {ospray => modules/cpu}/common/Data.ih | 0 .../cpu}/common/DifferentialGeometry.ih | 0 .../cpu}/common/FilterIntersect.ih | 0 {ospray => modules/cpu}/common/Future.cpp | 0 {ospray => modules/cpu}/common/Future.h | 0 {ospray => modules/cpu}/common/Group.cpp | 0 {ospray => modules/cpu}/common/Group.h | 0 {ospray => modules/cpu}/common/Group.ih | 0 {ospray => modules/cpu}/common/Group.ispc | 0 {ospray => modules/cpu}/common/Instance.cpp | 0 {ospray => modules/cpu}/common/Instance.h | 0 {ospray => modules/cpu}/common/Instance.ih | 0 {ospray => modules/cpu}/common/Instance.ispc | 0 {ospray => modules/cpu}/common/Intersect.ih | 0 {ospray => modules/cpu}/common/Managed.cpp | 0 {ospray => modules/cpu}/common/Managed.h | 0 .../cpu}/common/MotionTransform.cpp | 0 .../cpu}/common/MotionTransform.h | 0 .../cpu}/common/MotionTransform.ih | 0 {ospray => modules/cpu}/common/OSPCommon.ih | 0 {ospray => modules/cpu}/common/OSPCommon.ispc | 0 {ospray => modules/cpu}/common/OSPConfig.h.in | 0 {ospray => modules/cpu}/common/Ray.ih | 0 .../cpu}/common/VolumeIntervals.ih | 0 {ospray => modules/cpu}/common/World.cpp | 0 {ospray => modules/cpu}/common/World.h | 0 {ospray => modules/cpu}/common/World.ih | 0 {ospray => modules/cpu}/common/World.ispc | 0 {ospray => modules/cpu}/def_header.txt | 0 {ospray => modules/cpu}/fb/FrameBuffer.cpp | 0 {ospray => modules/cpu}/fb/FrameBuffer.h | 0 {ospray => modules/cpu}/fb/FrameBuffer.ih | 0 {ospray => modules/cpu}/fb/FrameBuffer.ispc | 0 .../cpu}/fb/FrameBufferView.cpp | 0 {ospray => modules/cpu}/fb/FrameBufferView.h | 0 {ospray => modules/cpu}/fb/FrameBufferView.ih | 0 {ospray => modules/cpu}/fb/ImageOp.cpp | 0 {ospray => modules/cpu}/fb/ImageOp.h | 0 {ospray => modules/cpu}/fb/LocalFB.cpp | 0 {ospray => modules/cpu}/fb/LocalFB.h | 0 {ospray => modules/cpu}/fb/LocalFB.ih | 0 {ospray => modules/cpu}/fb/LocalFB.ispc | 0 {ospray => modules/cpu}/fb/Tile.h | 0 {ospray => modules/cpu}/fb/Tile.ih | 0 {ospray => modules/cpu}/fb/TileError.cpp | 0 {ospray => modules/cpu}/fb/TileError.h | 0 {ospray => modules/cpu}/fb/frame_ops/Blur.cpp | 0 {ospray => modules/cpu}/fb/frame_ops/Blur.h | 0 .../cpu}/fb/frame_ops/Debug.cpp | 0 {ospray => modules/cpu}/fb/frame_ops/Debug.h | 0 .../cpu}/fb/frame_ops/Depth.cpp | 0 {ospray => modules/cpu}/fb/frame_ops/Depth.h | 0 {ospray => modules/cpu}/fb/frame_ops/SSAO.cpp | 0 {ospray => modules/cpu}/fb/frame_ops/SSAO.h | 0 .../cpu}/fb/frame_ops/SSAO.ispc | 0 {ospray => modules/cpu}/fb/registration.cpp | 0 {ospray => modules/cpu}/fb/registration.h | 0 .../cpu}/fb/tile_ops/SaveTiles.cpp | 0 .../cpu}/fb/tile_ops/SaveTiles.h | 0 .../cpu}/fb/tile_ops/ToneMapper.cpp | 0 .../cpu}/fb/tile_ops/ToneMapper.h | 0 .../cpu}/fb/tile_ops/ToneMapper.ispc | 0 {ospray => modules/cpu}/geometry/Boxes.cpp | 0 {ospray => modules/cpu}/geometry/Boxes.h | 0 {ospray => modules/cpu}/geometry/Boxes.ispc | 0 {ospray => modules/cpu}/geometry/Curves.cpp | 0 {ospray => modules/cpu}/geometry/Curves.h | 0 {ospray => modules/cpu}/geometry/Curves.ispc | 0 .../cpu}/geometry/GeometricModel.cpp | 0 .../cpu}/geometry/GeometricModel.h | 0 .../cpu}/geometry/GeometricModel.ih | 0 .../cpu}/geometry/GeometricModel.ispc | 0 {ospray => modules/cpu}/geometry/Geometry.cpp | 0 {ospray => modules/cpu}/geometry/Geometry.h | 0 {ospray => modules/cpu}/geometry/Geometry.ih | 0 .../cpu}/geometry/Geometry.ispc | 0 .../cpu}/geometry/Isosurfaces.cpp | 0 .../cpu}/geometry/Isosurfaces.h | 0 .../cpu}/geometry/Isosurfaces.ispc | 0 {ospray => modules/cpu}/geometry/Mesh.cpp | 2 +- {ospray => modules/cpu}/geometry/Mesh.h | 0 {ospray => modules/cpu}/geometry/Mesh.ispc | 0 {ospray => modules/cpu}/geometry/Planes.cpp | 0 {ospray => modules/cpu}/geometry/Planes.h | 0 {ospray => modules/cpu}/geometry/Planes.ispc | 0 {ospray => modules/cpu}/geometry/Spheres.cpp | 0 {ospray => modules/cpu}/geometry/Spheres.h | 0 {ospray => modules/cpu}/geometry/Spheres.ispc | 0 .../cpu}/geometry/Subdivision.cpp | 2 +- .../cpu}/geometry/Subdivision.h | 0 .../cpu}/geometry/Subdivision.ispc | 0 .../cpu}/geometry/registration.cpp | 0 .../cpu}/geometry/registration.h | 0 {ospray => modules/cpu}/ispc_symbols.txt | 36 +- {ospray => modules/cpu}/ispc_tasksys.cpp | 0 .../cpu}/lights/AmbientLight.cpp | 0 {ospray => modules/cpu}/lights/AmbientLight.h | 0 .../cpu}/lights/AmbientLight.ispc | 0 .../cpu}/lights/CylinderCapSolidAngleTable.ih | 0 .../cpu}/lights/CylinderLight.cpp | 0 .../cpu}/lights/CylinderLight.h | 0 .../cpu}/lights/CylinderLight.ispc | 0 .../cpu}/lights/DirectionalLight.cpp | 0 .../cpu}/lights/DirectionalLight.h | 0 .../cpu}/lights/DirectionalLight.ispc | 0 {ospray => modules/cpu}/lights/HDRILight.cpp | 0 {ospray => modules/cpu}/lights/HDRILight.h | 0 {ospray => modules/cpu}/lights/HDRILight.ispc | 0 {ospray => modules/cpu}/lights/Light.cpp | 0 {ospray => modules/cpu}/lights/Light.h | 0 {ospray => modules/cpu}/lights/Light.ih | 0 {ospray => modules/cpu}/lights/Light.ispc | 0 {ospray => modules/cpu}/lights/PointLight.cpp | 0 {ospray => modules/cpu}/lights/PointLight.h | 0 .../cpu}/lights/PointLight.ispc | 0 {ospray => modules/cpu}/lights/QuadLight.cpp | 0 {ospray => modules/cpu}/lights/QuadLight.h | 0 {ospray => modules/cpu}/lights/QuadLight.ispc | 0 .../cpu}/lights/SphericalQuadSampling.ih | 8 +- {ospray => modules/cpu}/lights/SpotLight.cpp | 0 {ospray => modules/cpu}/lights/SpotLight.h | 0 {ospray => modules/cpu}/lights/SpotLight.ispc | 0 .../cpu}/lights/SunSkyLight.cpp | 0 {ospray => modules/cpu}/lights/SunSkyLight.h | 0 .../cpu}/lights/registration.cpp | 0 {ospray => modules/cpu}/lights/registration.h | 0 .../cpu}/lights/sky_model/color_info.h | 0 .../cpu}/lights/sky_model/sky_model.cpp | 0 .../cpu}/lights/sky_model/sky_model.h | 0 .../lights/sky_model/sky_model_data_ciexyz.h | 0 .../lights/sky_model/sky_model_data_rgb.h | 0 .../sky_model/sky_model_data_spectral.h | 0 {ospray => modules/cpu}/math/AffineSpace.ih | 0 .../cpu}/math/Distribution1D.ih | 0 .../cpu}/math/Distribution1D.ispc | 0 .../cpu}/math/Distribution2D.ih | 0 .../cpu}/math/Distribution2D.ispc | 0 {ospray => modules/cpu}/math/LinearSpace.ih | 0 {ospray => modules/cpu}/math/halton.ih | 0 {ospray => modules/cpu}/math/halton.ispc | 0 {ospray => modules/cpu}/math/interpolation.ih | 0 {ospray => modules/cpu}/math/random.ih | 0 {ospray => modules/cpu}/math/sampling.ih | 0 {ospray => modules/cpu}/math/sobol.ih | 0 {ospray => modules/cpu}/math/sobol.ispc | 0 {ospray => modules/cpu}/math/spectrum.h | 0 {ospray => modules/cpu}/math/spectrum.ih | 0 {ospray => modules/cpu}/math/spectrum.ispc | 0 {ospray => modules/cpu}/pf/LUTPixelFilter.ih | 0 .../cpu}/pf/LUTPixelFilter.ispc | 0 {ospray => modules/cpu}/pf/PixelFilter.cpp | 0 {ospray => modules/cpu}/pf/PixelFilter.h | 0 {ospray => modules/cpu}/pf/PixelFilter.ih | 0 {ospray => modules/cpu}/pf/PixelFilter.ispc | 0 .../cpu}/render/LoadBalancer.cpp | 0 {ospray => modules/cpu}/render/LoadBalancer.h | 0 {ospray => modules/cpu}/render/Material.cpp | 0 {ospray => modules/cpu}/render/Material.h | 0 {ospray => modules/cpu}/render/Material.ih | 0 .../cpu}/render/MaterialType.ih | 0 {ospray => modules/cpu}/render/RenderTask.h | 0 {ospray => modules/cpu}/render/Renderer.cpp | 0 {ospray => modules/cpu}/render/Renderer.h | 0 {ospray => modules/cpu}/render/Renderer.ih | 0 {ospray => modules/cpu}/render/Renderer.ispc | 0 .../cpu}/render/ao/AORenderer.cpp | 0 .../cpu}/render/ao/AORenderer.h | 0 .../cpu}/render/ao/AORenderer.ih | 0 .../cpu}/render/ao/AORenderer.ispc | 0 {ospray => modules/cpu}/render/ao/surfaces.ih | 0 .../cpu}/render/ao/surfaces.ispc | 0 {ospray => modules/cpu}/render/ao/volumes.ih | 0 .../cpu}/render/ao/volumes.ispc | 0 {ospray => modules/cpu}/render/bsdfs/BSDF.ih | 0 .../cpu}/render/bsdfs/BeckmannDistribution.ih | 0 .../cpu}/render/bsdfs/Conductor.ih | 0 .../cpu}/render/bsdfs/Dielectric.ih | 0 .../cpu}/render/bsdfs/DielectricLayer.ih | 0 .../cpu}/render/bsdfs/Fresnel.ih | 0 .../cpu}/render/bsdfs/GGXDistribution.ih | 0 .../cpu}/render/bsdfs/Lambert.ih | 0 .../cpu}/render/bsdfs/LambertTransmission.ih | 0 .../cpu}/render/bsdfs/MicrofacetAlbedo.ih | 0 .../render/bsdfs/MicrofacetAlbedoTables.ih | 0 .../render/bsdfs/MicrofacetAlbedoTables.ispc | 0 .../cpu}/render/bsdfs/MicrofacetConductor.ih | 0 .../cpu}/render/bsdfs/MicrofacetDielectric.ih | 0 .../render/bsdfs/MicrofacetDielectricLayer.ih | 0 .../render/bsdfs/MicrofacetDistribution.ih | 0 .../cpu}/render/bsdfs/MicrofacetSheenLayer.ih | 0 .../cpu}/render/bsdfs/Minneart.ih | 0 .../cpu}/render/bsdfs/MultiBSDF.ih | 0 .../cpu}/render/bsdfs/Optics.ih | 0 .../cpu}/render/bsdfs/OrenNayar.ih | 0 .../render/bsdfs/PowerCosineDistribution.ih | 0 .../cpu}/render/bsdfs/Reflection.ih | 0 .../cpu}/render/bsdfs/RobustDielectric.ih | 0 .../cpu}/render/bsdfs/RobustThinDielectric.ih | 0 {ospray => modules/cpu}/render/bsdfs/Scale.ih | 0 .../cpu}/render/bsdfs/ShadingContext.ih | 0 .../cpu}/render/bsdfs/SheenDistribution.ih | 0 .../cpu}/render/bsdfs/Specular.ih | 0 .../cpu}/render/bsdfs/ThinDielectric.ih | 0 .../render/bsdfs/ThinMicrofacetDielectric.ih | 0 .../cpu}/render/bsdfs/Transmission.ih | 0 .../cpu}/render/bsdfs/Velvety.ih | 0 .../cpu}/render/debug/DebugRenderer.cpp | 0 .../cpu}/render/debug/DebugRenderer.h | 0 .../cpu}/render/debug/DebugRenderer.ispc | 0 .../cpu}/render/debug/DebugRendererType.h | 0 .../cpu}/render/materials/Alloy.cpp | 0 .../cpu}/render/materials/Alloy.h | 0 .../cpu}/render/materials/Alloy.ispc | 0 .../cpu}/render/materials/CarPaint.cpp | 0 .../cpu}/render/materials/CarPaint.h | 0 .../cpu}/render/materials/CarPaint.ispc | 0 .../cpu}/render/materials/Glass.cpp | 0 .../cpu}/render/materials/Glass.h | 0 .../cpu}/render/materials/Glass.ispc | 0 .../cpu}/render/materials/Luminous.cpp | 0 .../cpu}/render/materials/Luminous.h | 0 .../cpu}/render/materials/Luminous.ispc | 0 .../cpu}/render/materials/Material.ih | 0 .../cpu}/render/materials/Material.ispc | 0 .../cpu}/render/materials/Medium.ih | 0 .../cpu}/render/materials/Metal.cpp | 0 .../cpu}/render/materials/Metal.h | 0 .../cpu}/render/materials/Metal.ispc | 0 .../cpu}/render/materials/MetallicPaint.cpp | 0 .../cpu}/render/materials/MetallicPaint.h | 0 .../cpu}/render/materials/MetallicPaint.ispc | 0 .../cpu}/render/materials/Mix.cpp | 0 .../cpu}/render/materials/Mix.h | 0 .../cpu}/render/materials/Mix.ispc | 0 .../cpu}/render/materials/OBJ.cpp | 0 .../cpu}/render/materials/OBJ.h | 0 .../cpu}/render/materials/OBJ.ih | 0 .../cpu}/render/materials/OBJ.ispc | 0 .../cpu}/render/materials/Plastic.cpp | 0 .../cpu}/render/materials/Plastic.h | 0 .../cpu}/render/materials/Plastic.ispc | 0 .../cpu}/render/materials/Principled.cpp | 0 .../cpu}/render/materials/Principled.h | 0 .../cpu}/render/materials/Principled.ispc | 0 .../cpu}/render/materials/ThinGlass.cpp | 0 .../cpu}/render/materials/ThinGlass.h | 0 .../cpu}/render/materials/ThinGlass.ispc | 0 .../cpu}/render/materials/Velvet.cpp | 0 .../cpu}/render/materials/Velvet.h | 0 .../cpu}/render/materials/Velvet.ispc | 0 .../cpu}/render/pathtracer/GeometryLight.ih | 0 .../cpu}/render/pathtracer/GeometryLight.ispc | 0 .../render/pathtracer/NextEventEstimation.ih | 0 .../pathtracer/NextEventEstimation.ispc | 0 .../cpu}/render/pathtracer/PathSampler.ih | 0 .../cpu}/render/pathtracer/PathSampler.ispc | 0 .../cpu}/render/pathtracer/PathStructs.ih | 0 .../cpu}/render/pathtracer/PathTracer.cpp | 0 .../cpu}/render/pathtracer/PathTracer.h | 0 .../cpu}/render/pathtracer/PathTracer.ih | 0 .../cpu}/render/pathtracer/PathTracer.ispc | 0 .../render/pathtracer/PathTracerDefines.ih | 0 .../cpu}/render/pathtracer/PathTracerUtil.ih | 0 .../cpu}/render/pathtracer/Scattering.ih | 0 .../cpu}/render/pathtracer/ShadowCatcher.ih | 0 .../cpu}/render/pathtracer/ShadowCatcher.ispc | 0 .../render/pathtracer/TransparentShadow.ih | 0 .../render/pathtracer/TransparentShadow.ispc | 0 .../cpu}/render/pathtracer/VirtualLight.ih | 0 .../cpu}/render/pathtracer/VirtualLight.ispc | 0 .../pathtracer/volumes/HenyeyGreenstein.ih | 0 .../pathtracer/volumes/VolumeSampler.ih | 0 .../pathtracer/volumes/VolumeSampler.ispc | 0 .../cpu}/render/registration.cpp | 0 {ospray => modules/cpu}/render/registration.h | 0 .../cpu}/render/scivis/SciVis.cpp | 0 .../cpu}/render/scivis/SciVis.h | 0 .../cpu}/render/scivis/SciVis.ih | 0 .../cpu}/render/scivis/SciVis.ispc | 0 .../cpu}/render/scivis/lightAlpha.ispc | 0 .../cpu}/render/scivis/surfaces.ih | 0 .../cpu}/render/scivis/surfaces.ispc | 0 .../cpu}/render/scivis/volumes.ih | 0 .../cpu}/render/scivis/volumes.ispc | 0 .../cpu}/render/shaders/Flakes.ih | 0 .../cpu}/render/shaders/Noise.ih | 0 {ospray => modules/cpu}/render/util.h | 0 {ospray => modules/cpu}/render/util.ih | 0 {ospray => modules/cpu}/render/util.ispc | 0 {ospray => modules/cpu}/texture/Texture.cpp | 0 {ospray => modules/cpu}/texture/Texture.h | 0 {ospray => modules/cpu}/texture/Texture2D.cpp | 0 {ospray => modules/cpu}/texture/Texture2D.h | 0 {ospray => modules/cpu}/texture/Texture2D.ih | 0 .../cpu}/texture/Texture2D.ispc | 0 .../cpu}/texture/TextureParam.ih | 0 .../cpu}/texture/TextureParam.ispc | 0 .../cpu}/texture/TextureVolume.cpp | 0 .../cpu}/texture/TextureVolume.h | 0 .../cpu}/texture/TextureVolume.ispc | 0 .../cpu}/texture/registration.cpp | 0 .../cpu}/texture/registration.h | 0 {ospray => modules/cpu}/volume/Volume.cpp | 0 {ospray => modules/cpu}/volume/Volume.h | 2 +- {ospray => modules/cpu}/volume/Volume.ih | 0 {ospray => modules/cpu}/volume/Volume.ispc | 0 .../cpu}/volume/VolumetricModel.cpp | 0 .../cpu}/volume/VolumetricModel.h | 0 .../cpu}/volume/VolumetricModel.ih | 0 .../cpu}/volume/VolumetricModel.ispc | 0 .../LinearTransferFunction.cpp | 0 .../transferFunction/LinearTransferFunction.h | 0 .../LinearTransferFunction.ih | 0 .../LinearTransferFunction.ispc | 0 .../transferFunction/TransferFunction.cpp | 0 .../transferFunction/TransferFunction.h | 0 .../transferFunction/TransferFunction.ih | 0 .../transferFunction/TransferFunction.ispc | 0 .../volume/transferFunction/registration.cpp | 0 .../volume/transferFunction/registration.h | 0 modules/denoiser/CMakeLists.txt | 6 +- modules/denoiser/DenoiseFrameOp.h | 4 +- modules/mpi/CMakeLists.txt | 14 +- modules/mpi/common/CMakeLists.txt | 3 +- modules/mpi/ospray/CMakeLists.txt | 3 +- modules/mpi/ospray/MPIDistributedDevice.cpp | 2 +- modules/mpi/ospray/MPIDistributedDevice.h | 2 +- modules/mpi/ospray/MPIOffloadWorker.cpp | 2 +- modules/mpi/ospray/ModuleInit.cpp | 2 +- .../mpi/ospray/common/DistributedWorld.cpp | 2 +- modules/mpi/ospray/common/DistributedWorld.ih | 2 +- modules/mpi/testing/CMakeLists.txt | 2 +- modules/multiDevice/CMakeLists.txt | 5 +- modules/multiDevice/ModuleInit.cpp | 2 +- modules/multiDevice/MultiDevice.h | 2 +- .../pluggableGeometryExample/CMakeLists.txt | 4 +- .../ospray/CMakeLists.txt | 4 +- .../ospray/geometry/BilinearPatches.cpp | 2 +- .../ospray/geometry/BilinearPatches.h | 4 +- ospray/CMakeLists.txt | 312 ----------------- ospray/api/API.cpp | 12 +- ospray/api/Device.cpp | 6 +- ospray/common/OSPCommon.h | 2 +- ospray/include/ospray/ospray.h | 2 +- scripts/release/linux.sh | 8 +- scripts/superbuild/CMakeLists.txt | 3 +- 386 files changed, 488 insertions(+), 462 deletions(-) create mode 100644 modules/cpu/CMakeLists.txt rename {ospray/api => modules/cpu}/ISPCDevice.cpp (99%) rename {ospray/api => modules/cpu}/ISPCDevice.h (100%) rename {ospray/api => modules/cpu}/ISPCDevice.ispc (100%) rename {ospray => modules/cpu}/camera/Camera.cpp (100%) rename {ospray => modules/cpu}/camera/Camera.h (100%) rename {ospray => modules/cpu}/camera/Camera.ih (100%) rename {ospray => modules/cpu}/camera/Camera.ispc (100%) rename {ospray => modules/cpu}/camera/OrthographicCamera.cpp (100%) rename {ospray => modules/cpu}/camera/OrthographicCamera.h (100%) rename {ospray => modules/cpu}/camera/OrthographicCamera.ispc (100%) rename {ospray => modules/cpu}/camera/PanoramicCamera.cpp (100%) rename {ospray => modules/cpu}/camera/PanoramicCamera.h (100%) rename {ospray => modules/cpu}/camera/PanoramicCamera.ispc (100%) rename {ospray => modules/cpu}/camera/PerspectiveCamera.cpp (100%) rename {ospray => modules/cpu}/camera/PerspectiveCamera.h (100%) rename {ospray => modules/cpu}/camera/PerspectiveCamera.ispc (100%) rename {ospray => modules/cpu}/camera/registration.cpp (100%) rename {ospray => modules/cpu}/camera/registration.h (100%) rename {ospray => modules/cpu}/common/Clipping.ih (100%) rename {ospray => modules/cpu}/common/Data.cpp (100%) rename {ospray => modules/cpu}/common/Data.h (100%) rename {ospray => modules/cpu}/common/Data.ih (100%) rename {ospray => modules/cpu}/common/DifferentialGeometry.ih (100%) rename {ospray => modules/cpu}/common/FilterIntersect.ih (100%) rename {ospray => modules/cpu}/common/Future.cpp (100%) rename {ospray => modules/cpu}/common/Future.h (100%) rename {ospray => modules/cpu}/common/Group.cpp (100%) rename {ospray => modules/cpu}/common/Group.h (100%) rename {ospray => modules/cpu}/common/Group.ih (100%) rename {ospray => modules/cpu}/common/Group.ispc (100%) rename {ospray => modules/cpu}/common/Instance.cpp (100%) rename {ospray => modules/cpu}/common/Instance.h (100%) rename {ospray => modules/cpu}/common/Instance.ih (100%) rename {ospray => modules/cpu}/common/Instance.ispc (100%) rename {ospray => modules/cpu}/common/Intersect.ih (100%) rename {ospray => modules/cpu}/common/Managed.cpp (100%) rename {ospray => modules/cpu}/common/Managed.h (100%) rename {ospray => modules/cpu}/common/MotionTransform.cpp (100%) rename {ospray => modules/cpu}/common/MotionTransform.h (100%) rename {ospray => modules/cpu}/common/MotionTransform.ih (100%) rename {ospray => modules/cpu}/common/OSPCommon.ih (100%) rename {ospray => modules/cpu}/common/OSPCommon.ispc (100%) rename {ospray => modules/cpu}/common/OSPConfig.h.in (100%) rename {ospray => modules/cpu}/common/Ray.ih (100%) rename {ospray => modules/cpu}/common/VolumeIntervals.ih (100%) rename {ospray => modules/cpu}/common/World.cpp (100%) rename {ospray => modules/cpu}/common/World.h (100%) rename {ospray => modules/cpu}/common/World.ih (100%) rename {ospray => modules/cpu}/common/World.ispc (100%) rename {ospray => modules/cpu}/def_header.txt (100%) rename {ospray => modules/cpu}/fb/FrameBuffer.cpp (100%) rename {ospray => modules/cpu}/fb/FrameBuffer.h (100%) rename {ospray => modules/cpu}/fb/FrameBuffer.ih (100%) rename {ospray => modules/cpu}/fb/FrameBuffer.ispc (100%) rename {ospray => modules/cpu}/fb/FrameBufferView.cpp (100%) rename {ospray => modules/cpu}/fb/FrameBufferView.h (100%) rename {ospray => modules/cpu}/fb/FrameBufferView.ih (100%) rename {ospray => modules/cpu}/fb/ImageOp.cpp (100%) rename {ospray => modules/cpu}/fb/ImageOp.h (100%) rename {ospray => modules/cpu}/fb/LocalFB.cpp (100%) rename {ospray => modules/cpu}/fb/LocalFB.h (100%) rename {ospray => modules/cpu}/fb/LocalFB.ih (100%) rename {ospray => modules/cpu}/fb/LocalFB.ispc (100%) rename {ospray => modules/cpu}/fb/Tile.h (100%) rename {ospray => modules/cpu}/fb/Tile.ih (100%) rename {ospray => modules/cpu}/fb/TileError.cpp (100%) rename {ospray => modules/cpu}/fb/TileError.h (100%) rename {ospray => modules/cpu}/fb/frame_ops/Blur.cpp (100%) rename {ospray => modules/cpu}/fb/frame_ops/Blur.h (100%) rename {ospray => modules/cpu}/fb/frame_ops/Debug.cpp (100%) rename {ospray => modules/cpu}/fb/frame_ops/Debug.h (100%) rename {ospray => modules/cpu}/fb/frame_ops/Depth.cpp (100%) rename {ospray => modules/cpu}/fb/frame_ops/Depth.h (100%) rename {ospray => modules/cpu}/fb/frame_ops/SSAO.cpp (100%) rename {ospray => modules/cpu}/fb/frame_ops/SSAO.h (100%) rename {ospray => modules/cpu}/fb/frame_ops/SSAO.ispc (100%) rename {ospray => modules/cpu}/fb/registration.cpp (100%) rename {ospray => modules/cpu}/fb/registration.h (100%) rename {ospray => modules/cpu}/fb/tile_ops/SaveTiles.cpp (100%) rename {ospray => modules/cpu}/fb/tile_ops/SaveTiles.h (100%) rename {ospray => modules/cpu}/fb/tile_ops/ToneMapper.cpp (100%) rename {ospray => modules/cpu}/fb/tile_ops/ToneMapper.h (100%) rename {ospray => modules/cpu}/fb/tile_ops/ToneMapper.ispc (100%) rename {ospray => modules/cpu}/geometry/Boxes.cpp (100%) rename {ospray => modules/cpu}/geometry/Boxes.h (100%) rename {ospray => modules/cpu}/geometry/Boxes.ispc (100%) rename {ospray => modules/cpu}/geometry/Curves.cpp (100%) rename {ospray => modules/cpu}/geometry/Curves.h (100%) rename {ospray => modules/cpu}/geometry/Curves.ispc (100%) rename {ospray => modules/cpu}/geometry/GeometricModel.cpp (100%) rename {ospray => modules/cpu}/geometry/GeometricModel.h (100%) rename {ospray => modules/cpu}/geometry/GeometricModel.ih (100%) rename {ospray => modules/cpu}/geometry/GeometricModel.ispc (100%) rename {ospray => modules/cpu}/geometry/Geometry.cpp (100%) rename {ospray => modules/cpu}/geometry/Geometry.h (100%) rename {ospray => modules/cpu}/geometry/Geometry.ih (100%) rename {ospray => modules/cpu}/geometry/Geometry.ispc (100%) rename {ospray => modules/cpu}/geometry/Isosurfaces.cpp (100%) rename {ospray => modules/cpu}/geometry/Isosurfaces.h (100%) rename {ospray => modules/cpu}/geometry/Isosurfaces.ispc (100%) rename {ospray => modules/cpu}/geometry/Mesh.cpp (99%) rename {ospray => modules/cpu}/geometry/Mesh.h (100%) rename {ospray => modules/cpu}/geometry/Mesh.ispc (100%) rename {ospray => modules/cpu}/geometry/Planes.cpp (100%) rename {ospray => modules/cpu}/geometry/Planes.h (100%) rename {ospray => modules/cpu}/geometry/Planes.ispc (100%) rename {ospray => modules/cpu}/geometry/Spheres.cpp (100%) rename {ospray => modules/cpu}/geometry/Spheres.h (100%) rename {ospray => modules/cpu}/geometry/Spheres.ispc (100%) rename {ospray => modules/cpu}/geometry/Subdivision.cpp (99%) rename {ospray => modules/cpu}/geometry/Subdivision.h (100%) rename {ospray => modules/cpu}/geometry/Subdivision.ispc (100%) rename {ospray => modules/cpu}/geometry/registration.cpp (100%) rename {ospray => modules/cpu}/geometry/registration.h (100%) rename {ospray => modules/cpu}/ispc_symbols.txt (98%) rename {ospray => modules/cpu}/ispc_tasksys.cpp (100%) rename {ospray => modules/cpu}/lights/AmbientLight.cpp (100%) rename {ospray => modules/cpu}/lights/AmbientLight.h (100%) rename {ospray => modules/cpu}/lights/AmbientLight.ispc (100%) rename {ospray => modules/cpu}/lights/CylinderCapSolidAngleTable.ih (100%) rename {ospray => modules/cpu}/lights/CylinderLight.cpp (100%) rename {ospray => modules/cpu}/lights/CylinderLight.h (100%) rename {ospray => modules/cpu}/lights/CylinderLight.ispc (100%) rename {ospray => modules/cpu}/lights/DirectionalLight.cpp (100%) rename {ospray => modules/cpu}/lights/DirectionalLight.h (100%) rename {ospray => modules/cpu}/lights/DirectionalLight.ispc (100%) rename {ospray => modules/cpu}/lights/HDRILight.cpp (100%) rename {ospray => modules/cpu}/lights/HDRILight.h (100%) rename {ospray => modules/cpu}/lights/HDRILight.ispc (100%) rename {ospray => modules/cpu}/lights/Light.cpp (100%) rename {ospray => modules/cpu}/lights/Light.h (100%) rename {ospray => modules/cpu}/lights/Light.ih (100%) rename {ospray => modules/cpu}/lights/Light.ispc (100%) rename {ospray => modules/cpu}/lights/PointLight.cpp (100%) rename {ospray => modules/cpu}/lights/PointLight.h (100%) rename {ospray => modules/cpu}/lights/PointLight.ispc (100%) rename {ospray => modules/cpu}/lights/QuadLight.cpp (100%) rename {ospray => modules/cpu}/lights/QuadLight.h (100%) rename {ospray => modules/cpu}/lights/QuadLight.ispc (100%) rename {ospray => modules/cpu}/lights/SphericalQuadSampling.ih (97%) rename {ospray => modules/cpu}/lights/SpotLight.cpp (100%) rename {ospray => modules/cpu}/lights/SpotLight.h (100%) rename {ospray => modules/cpu}/lights/SpotLight.ispc (100%) rename {ospray => modules/cpu}/lights/SunSkyLight.cpp (100%) rename {ospray => modules/cpu}/lights/SunSkyLight.h (100%) rename {ospray => modules/cpu}/lights/registration.cpp (100%) rename {ospray => modules/cpu}/lights/registration.h (100%) rename {ospray => modules/cpu}/lights/sky_model/color_info.h (100%) rename {ospray => modules/cpu}/lights/sky_model/sky_model.cpp (100%) rename {ospray => modules/cpu}/lights/sky_model/sky_model.h (100%) rename {ospray => modules/cpu}/lights/sky_model/sky_model_data_ciexyz.h (100%) rename {ospray => modules/cpu}/lights/sky_model/sky_model_data_rgb.h (100%) rename {ospray => modules/cpu}/lights/sky_model/sky_model_data_spectral.h (100%) rename {ospray => modules/cpu}/math/AffineSpace.ih (100%) rename {ospray => modules/cpu}/math/Distribution1D.ih (100%) rename {ospray => modules/cpu}/math/Distribution1D.ispc (100%) rename {ospray => modules/cpu}/math/Distribution2D.ih (100%) rename {ospray => modules/cpu}/math/Distribution2D.ispc (100%) rename {ospray => modules/cpu}/math/LinearSpace.ih (100%) rename {ospray => modules/cpu}/math/halton.ih (100%) rename {ospray => modules/cpu}/math/halton.ispc (100%) rename {ospray => modules/cpu}/math/interpolation.ih (100%) rename {ospray => modules/cpu}/math/random.ih (100%) rename {ospray => modules/cpu}/math/sampling.ih (100%) rename {ospray => modules/cpu}/math/sobol.ih (100%) rename {ospray => modules/cpu}/math/sobol.ispc (100%) rename {ospray => modules/cpu}/math/spectrum.h (100%) rename {ospray => modules/cpu}/math/spectrum.ih (100%) rename {ospray => modules/cpu}/math/spectrum.ispc (100%) rename {ospray => modules/cpu}/pf/LUTPixelFilter.ih (100%) rename {ospray => modules/cpu}/pf/LUTPixelFilter.ispc (100%) rename {ospray => modules/cpu}/pf/PixelFilter.cpp (100%) rename {ospray => modules/cpu}/pf/PixelFilter.h (100%) rename {ospray => modules/cpu}/pf/PixelFilter.ih (100%) rename {ospray => modules/cpu}/pf/PixelFilter.ispc (100%) rename {ospray => modules/cpu}/render/LoadBalancer.cpp (100%) rename {ospray => modules/cpu}/render/LoadBalancer.h (100%) rename {ospray => modules/cpu}/render/Material.cpp (100%) rename {ospray => modules/cpu}/render/Material.h (100%) rename {ospray => modules/cpu}/render/Material.ih (100%) rename {ospray => modules/cpu}/render/MaterialType.ih (100%) rename {ospray => modules/cpu}/render/RenderTask.h (100%) rename {ospray => modules/cpu}/render/Renderer.cpp (100%) rename {ospray => modules/cpu}/render/Renderer.h (100%) rename {ospray => modules/cpu}/render/Renderer.ih (100%) rename {ospray => modules/cpu}/render/Renderer.ispc (100%) rename {ospray => modules/cpu}/render/ao/AORenderer.cpp (100%) rename {ospray => modules/cpu}/render/ao/AORenderer.h (100%) rename {ospray => modules/cpu}/render/ao/AORenderer.ih (100%) rename {ospray => modules/cpu}/render/ao/AORenderer.ispc (100%) rename {ospray => modules/cpu}/render/ao/surfaces.ih (100%) rename {ospray => modules/cpu}/render/ao/surfaces.ispc (100%) rename {ospray => modules/cpu}/render/ao/volumes.ih (100%) rename {ospray => modules/cpu}/render/ao/volumes.ispc (100%) rename {ospray => modules/cpu}/render/bsdfs/BSDF.ih (100%) rename {ospray => modules/cpu}/render/bsdfs/BeckmannDistribution.ih (100%) rename {ospray => modules/cpu}/render/bsdfs/Conductor.ih (100%) rename {ospray => modules/cpu}/render/bsdfs/Dielectric.ih (100%) rename {ospray => modules/cpu}/render/bsdfs/DielectricLayer.ih (100%) rename {ospray => modules/cpu}/render/bsdfs/Fresnel.ih (100%) rename {ospray => modules/cpu}/render/bsdfs/GGXDistribution.ih (100%) rename {ospray => modules/cpu}/render/bsdfs/Lambert.ih (100%) rename {ospray => modules/cpu}/render/bsdfs/LambertTransmission.ih (100%) rename {ospray => modules/cpu}/render/bsdfs/MicrofacetAlbedo.ih (100%) rename {ospray => modules/cpu}/render/bsdfs/MicrofacetAlbedoTables.ih (100%) rename {ospray => modules/cpu}/render/bsdfs/MicrofacetAlbedoTables.ispc (100%) rename {ospray => modules/cpu}/render/bsdfs/MicrofacetConductor.ih (100%) rename {ospray => modules/cpu}/render/bsdfs/MicrofacetDielectric.ih (100%) rename {ospray => modules/cpu}/render/bsdfs/MicrofacetDielectricLayer.ih (100%) rename {ospray => modules/cpu}/render/bsdfs/MicrofacetDistribution.ih (100%) rename {ospray => modules/cpu}/render/bsdfs/MicrofacetSheenLayer.ih (100%) rename {ospray => modules/cpu}/render/bsdfs/Minneart.ih (100%) rename {ospray => modules/cpu}/render/bsdfs/MultiBSDF.ih (100%) rename {ospray => modules/cpu}/render/bsdfs/Optics.ih (100%) rename {ospray => modules/cpu}/render/bsdfs/OrenNayar.ih (100%) rename {ospray => modules/cpu}/render/bsdfs/PowerCosineDistribution.ih (100%) rename {ospray => modules/cpu}/render/bsdfs/Reflection.ih (100%) rename {ospray => modules/cpu}/render/bsdfs/RobustDielectric.ih (100%) rename {ospray => modules/cpu}/render/bsdfs/RobustThinDielectric.ih (100%) rename {ospray => modules/cpu}/render/bsdfs/Scale.ih (100%) rename {ospray => modules/cpu}/render/bsdfs/ShadingContext.ih (100%) rename {ospray => modules/cpu}/render/bsdfs/SheenDistribution.ih (100%) rename {ospray => modules/cpu}/render/bsdfs/Specular.ih (100%) rename {ospray => modules/cpu}/render/bsdfs/ThinDielectric.ih (100%) rename {ospray => modules/cpu}/render/bsdfs/ThinMicrofacetDielectric.ih (100%) rename {ospray => modules/cpu}/render/bsdfs/Transmission.ih (100%) rename {ospray => modules/cpu}/render/bsdfs/Velvety.ih (100%) rename {ospray => modules/cpu}/render/debug/DebugRenderer.cpp (100%) rename {ospray => modules/cpu}/render/debug/DebugRenderer.h (100%) rename {ospray => modules/cpu}/render/debug/DebugRenderer.ispc (100%) rename {ospray => modules/cpu}/render/debug/DebugRendererType.h (100%) rename {ospray => modules/cpu}/render/materials/Alloy.cpp (100%) rename {ospray => modules/cpu}/render/materials/Alloy.h (100%) rename {ospray => modules/cpu}/render/materials/Alloy.ispc (100%) rename {ospray => modules/cpu}/render/materials/CarPaint.cpp (100%) rename {ospray => modules/cpu}/render/materials/CarPaint.h (100%) rename {ospray => modules/cpu}/render/materials/CarPaint.ispc (100%) rename {ospray => modules/cpu}/render/materials/Glass.cpp (100%) rename {ospray => modules/cpu}/render/materials/Glass.h (100%) rename {ospray => modules/cpu}/render/materials/Glass.ispc (100%) rename {ospray => modules/cpu}/render/materials/Luminous.cpp (100%) rename {ospray => modules/cpu}/render/materials/Luminous.h (100%) rename {ospray => modules/cpu}/render/materials/Luminous.ispc (100%) rename {ospray => modules/cpu}/render/materials/Material.ih (100%) rename {ospray => modules/cpu}/render/materials/Material.ispc (100%) rename {ospray => modules/cpu}/render/materials/Medium.ih (100%) rename {ospray => modules/cpu}/render/materials/Metal.cpp (100%) rename {ospray => modules/cpu}/render/materials/Metal.h (100%) rename {ospray => modules/cpu}/render/materials/Metal.ispc (100%) rename {ospray => modules/cpu}/render/materials/MetallicPaint.cpp (100%) rename {ospray => modules/cpu}/render/materials/MetallicPaint.h (100%) rename {ospray => modules/cpu}/render/materials/MetallicPaint.ispc (100%) rename {ospray => modules/cpu}/render/materials/Mix.cpp (100%) rename {ospray => modules/cpu}/render/materials/Mix.h (100%) rename {ospray => modules/cpu}/render/materials/Mix.ispc (100%) rename {ospray => modules/cpu}/render/materials/OBJ.cpp (100%) rename {ospray => modules/cpu}/render/materials/OBJ.h (100%) rename {ospray => modules/cpu}/render/materials/OBJ.ih (100%) rename {ospray => modules/cpu}/render/materials/OBJ.ispc (100%) rename {ospray => modules/cpu}/render/materials/Plastic.cpp (100%) rename {ospray => modules/cpu}/render/materials/Plastic.h (100%) rename {ospray => modules/cpu}/render/materials/Plastic.ispc (100%) rename {ospray => modules/cpu}/render/materials/Principled.cpp (100%) rename {ospray => modules/cpu}/render/materials/Principled.h (100%) rename {ospray => modules/cpu}/render/materials/Principled.ispc (100%) rename {ospray => modules/cpu}/render/materials/ThinGlass.cpp (100%) rename {ospray => modules/cpu}/render/materials/ThinGlass.h (100%) rename {ospray => modules/cpu}/render/materials/ThinGlass.ispc (100%) rename {ospray => modules/cpu}/render/materials/Velvet.cpp (100%) rename {ospray => modules/cpu}/render/materials/Velvet.h (100%) rename {ospray => modules/cpu}/render/materials/Velvet.ispc (100%) rename {ospray => modules/cpu}/render/pathtracer/GeometryLight.ih (100%) rename {ospray => modules/cpu}/render/pathtracer/GeometryLight.ispc (100%) rename {ospray => modules/cpu}/render/pathtracer/NextEventEstimation.ih (100%) rename {ospray => modules/cpu}/render/pathtracer/NextEventEstimation.ispc (100%) rename {ospray => modules/cpu}/render/pathtracer/PathSampler.ih (100%) rename {ospray => modules/cpu}/render/pathtracer/PathSampler.ispc (100%) rename {ospray => modules/cpu}/render/pathtracer/PathStructs.ih (100%) rename {ospray => modules/cpu}/render/pathtracer/PathTracer.cpp (100%) rename {ospray => modules/cpu}/render/pathtracer/PathTracer.h (100%) rename {ospray => modules/cpu}/render/pathtracer/PathTracer.ih (100%) rename {ospray => modules/cpu}/render/pathtracer/PathTracer.ispc (100%) rename {ospray => modules/cpu}/render/pathtracer/PathTracerDefines.ih (100%) rename {ospray => modules/cpu}/render/pathtracer/PathTracerUtil.ih (100%) rename {ospray => modules/cpu}/render/pathtracer/Scattering.ih (100%) rename {ospray => modules/cpu}/render/pathtracer/ShadowCatcher.ih (100%) rename {ospray => modules/cpu}/render/pathtracer/ShadowCatcher.ispc (100%) rename {ospray => modules/cpu}/render/pathtracer/TransparentShadow.ih (100%) rename {ospray => modules/cpu}/render/pathtracer/TransparentShadow.ispc (100%) rename {ospray => modules/cpu}/render/pathtracer/VirtualLight.ih (100%) rename {ospray => modules/cpu}/render/pathtracer/VirtualLight.ispc (100%) rename {ospray => modules/cpu}/render/pathtracer/volumes/HenyeyGreenstein.ih (100%) rename {ospray => modules/cpu}/render/pathtracer/volumes/VolumeSampler.ih (100%) rename {ospray => modules/cpu}/render/pathtracer/volumes/VolumeSampler.ispc (100%) rename {ospray => modules/cpu}/render/registration.cpp (100%) rename {ospray => modules/cpu}/render/registration.h (100%) rename {ospray => modules/cpu}/render/scivis/SciVis.cpp (100%) rename {ospray => modules/cpu}/render/scivis/SciVis.h (100%) rename {ospray => modules/cpu}/render/scivis/SciVis.ih (100%) rename {ospray => modules/cpu}/render/scivis/SciVis.ispc (100%) rename {ospray => modules/cpu}/render/scivis/lightAlpha.ispc (100%) rename {ospray => modules/cpu}/render/scivis/surfaces.ih (100%) rename {ospray => modules/cpu}/render/scivis/surfaces.ispc (100%) rename {ospray => modules/cpu}/render/scivis/volumes.ih (100%) rename {ospray => modules/cpu}/render/scivis/volumes.ispc (100%) rename {ospray => modules/cpu}/render/shaders/Flakes.ih (100%) rename {ospray => modules/cpu}/render/shaders/Noise.ih (100%) rename {ospray => modules/cpu}/render/util.h (100%) rename {ospray => modules/cpu}/render/util.ih (100%) rename {ospray => modules/cpu}/render/util.ispc (100%) rename {ospray => modules/cpu}/texture/Texture.cpp (100%) rename {ospray => modules/cpu}/texture/Texture.h (100%) rename {ospray => modules/cpu}/texture/Texture2D.cpp (100%) rename {ospray => modules/cpu}/texture/Texture2D.h (100%) rename {ospray => modules/cpu}/texture/Texture2D.ih (100%) rename {ospray => modules/cpu}/texture/Texture2D.ispc (100%) rename {ospray => modules/cpu}/texture/TextureParam.ih (100%) rename {ospray => modules/cpu}/texture/TextureParam.ispc (100%) rename {ospray => modules/cpu}/texture/TextureVolume.cpp (100%) rename {ospray => modules/cpu}/texture/TextureVolume.h (100%) rename {ospray => modules/cpu}/texture/TextureVolume.ispc (100%) rename {ospray => modules/cpu}/texture/registration.cpp (100%) rename {ospray => modules/cpu}/texture/registration.h (100%) rename {ospray => modules/cpu}/volume/Volume.cpp (100%) rename {ospray => modules/cpu}/volume/Volume.h (97%) rename {ospray => modules/cpu}/volume/Volume.ih (100%) rename {ospray => modules/cpu}/volume/Volume.ispc (100%) rename {ospray => modules/cpu}/volume/VolumetricModel.cpp (100%) rename {ospray => modules/cpu}/volume/VolumetricModel.h (100%) rename {ospray => modules/cpu}/volume/VolumetricModel.ih (100%) rename {ospray => modules/cpu}/volume/VolumetricModel.ispc (100%) rename {ospray => modules/cpu}/volume/transferFunction/LinearTransferFunction.cpp (100%) rename {ospray => modules/cpu}/volume/transferFunction/LinearTransferFunction.h (100%) rename {ospray => modules/cpu}/volume/transferFunction/LinearTransferFunction.ih (100%) rename {ospray => modules/cpu}/volume/transferFunction/LinearTransferFunction.ispc (100%) rename {ospray => modules/cpu}/volume/transferFunction/TransferFunction.cpp (100%) rename {ospray => modules/cpu}/volume/transferFunction/TransferFunction.h (100%) rename {ospray => modules/cpu}/volume/transferFunction/TransferFunction.ih (100%) rename {ospray => modules/cpu}/volume/transferFunction/TransferFunction.ispc (100%) rename {ospray => modules/cpu}/volume/transferFunction/registration.cpp (100%) rename {ospray => modules/cpu}/volume/transferFunction/registration.h (100%) diff --git a/.gitignore b/.gitignore index 73e7930bfb..f069c56858 100644 --- a/.gitignore +++ b/.gitignore @@ -23,11 +23,12 @@ ispc readme.pdf !/modules /modules/* -!/modules/pluggableGeometryExample -!/modules/simdGeometryExample +!/modules/cpu !/modules/denoiser !/modules/mpi !/modules/multiDevice +!/modules/pluggableGeometryExample +!/modules/simdGeometryExample *.vim .idea/ premake.local.* diff --git a/CHANGELOG.md b/CHANGELOG.md index 22072ef2d3..268d316e72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ Version History - Add support for multi-segment deformation motion blur for `mesh` geometry +- Moved ISPC module to `modules/` folder and renamed the module to + `cpu` +- Refactored CMake variable names for enabling app categories + - OSPRAY_APPS_BENCHMARK refactored to OSPRAY_ENABLE_APPS_BENCHMARK + - OSPRAY_APPS_EXAMPLES refactored to OSPRAY_ENABLE_APPS_EXAMPLES + - OSPRAY_APPS_TUTORIALS refactored to OSPRAY_ENABLE_APPS_TUTORIALS + - OSPRAY_APPS_TESTING refactored to OSPRAY_ENABLE_APPS_TESTING ### Changes in v2.8.0: diff --git a/CMakeLists.txt b/CMakeLists.txt index 13ed44e643..e8ad6af55a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -## Copyright 2009-2020 Intel Corporation +## Copyright 2009-2021 Intel Corporation ## SPDX-License-Identifier: Apache-2.0 cmake_minimum_required(VERSION 3.1) @@ -54,12 +54,12 @@ endif() ## Main OSPRay library ## add_subdirectory(ospray) -## OSPRay sample apps ## -add_subdirectory(apps) - ## Modules ## add_subdirectory(modules) +## OSPRay sample apps ## +add_subdirectory(apps) + ## Regression test images target ## add_subdirectory(test_image_data) diff --git a/README.md b/README.md index 46be3d289d..0546946951 100644 --- a/README.md +++ b/README.md @@ -341,8 +341,8 @@ Advanced users may want to link to additional targets which are exported in OSPRayā€™s CMake config, which includes all installed modules. All targets built with OSPRay are exported in the `ospray::` namespace, therefore all targets locally used in the OSPRay source tree can be -accessed from an install. For example, `ospray_module_ispc` can be -consumed directly via the `ospray::ospray_module_ispc` target. All +accessed from an install. For example, `ospray_module_cpu` can be +consumed directly via the `ospray::ospray_module_cpu` target. All targets have their libraries, includes, and definitions attached to them for public consumption (please [report bugs](#ospray-support-and-contact) if this is broken!). @@ -3406,7 +3406,7 @@ when running cmake with ``` sh cmake \ -DOSPRAY_ENABLE_APPS=ON \ - -DOSPRAY_APPS_TUTORIALS=ON \ + -DOSPRAY_ENABLE_APPS_TUTORIALS=ON \ -DOSPRAY_MODULE_MPI=ON \ -DOSPRAY_MPI_BUILD_TUTORIALS=ON \ diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt index 8d04bb23d4..915bb211bc 100644 --- a/apps/CMakeLists.txt +++ b/apps/CMakeLists.txt @@ -1,43 +1,11 @@ -## Copyright 2009-2020 Intel Corporation +## Copyright 2009-2021 Intel Corporation ## SPDX-License-Identifier: Apache-2.0 -cmake_dependent_option( - OSPRAY_APPS_BENCHMARK - "Enable building, installing, and packaging of benchmark tools." - ON - OSPRAY_ENABLE_APPS - OFF -) - -cmake_dependent_option( - OSPRAY_APPS_EXAMPLES - "Enable building, installing, and packaging of ospExamples." - ON - OSPRAY_ENABLE_APPS - OFF -) - -cmake_dependent_option( - OSPRAY_APPS_TUTORIALS - "Enable building, installing, and packaging of tutorial apps." - ON - OSPRAY_ENABLE_APPS - OFF -) - -cmake_dependent_option( - OSPRAY_APPS_TESTING - "Enable building, installing, and packaging of test tools." - ON - OSPRAY_ENABLE_APPS - OFF -) - cmake_dependent_option( OSPRAY_APPS_ENABLE_GLM "Build ospray_cpp glm tests/tutorial" OFF - "OSPRAY_APPS_TESTING OR OSPRAY_APPS_TUTORIALS" + "OSPRAY_ENABLE_APPS_TESTING OR OSPRAY_ENABLE_APPS_TUTORIALS" OFF ) diff --git a/apps/common/CMakeLists.txt b/apps/common/CMakeLists.txt index 5094b5c263..34c571ff9d 100644 --- a/apps/common/CMakeLists.txt +++ b/apps/common/CMakeLists.txt @@ -1,9 +1,9 @@ ## Copyright 2009-2021 Intel Corporation ## SPDX-License-Identifier: Apache-2.0 -if (NOT OSPRAY_APPS_BENCHMARK AND - NOT OSPRAY_APPS_EXAMPLES AND - NOT OSPRAY_APPS_TESTING) +if (NOT OSPRAY_ENABLE_APPS_BENCHMARK AND + NOT OSPRAY_ENABLE_APPS_EXAMPLES AND + NOT OSPRAY_ENABLE_APPS_TESTING) return() endif() diff --git a/apps/common/external/CMakeLists.txt b/apps/common/external/CMakeLists.txt index 950510e6cb..3acd5fa3b6 100644 --- a/apps/common/external/CMakeLists.txt +++ b/apps/common/external/CMakeLists.txt @@ -3,7 +3,7 @@ ospray_disable_compiler_warnings() -if (OSPRAY_APPS_EXAMPLES) +if (OSPRAY_ENABLE_APPS_EXAMPLES) add_subdirectory(imgui) endif() diff --git a/apps/ospBenchmark/CMakeLists.txt b/apps/ospBenchmark/CMakeLists.txt index 882501706e..7d68b87c01 100644 --- a/apps/ospBenchmark/CMakeLists.txt +++ b/apps/ospBenchmark/CMakeLists.txt @@ -1,7 +1,7 @@ ## Copyright 2018-2021 Intel Corporation ## SPDX-License-Identifier: Apache-2.0 -if (NOT OSPRAY_APPS_BENCHMARK) +if (NOT OSPRAY_ENABLE_APPS_BENCHMARK) return() endif() diff --git a/apps/ospExamples/CMakeLists.txt b/apps/ospExamples/CMakeLists.txt index 2c988bd6cb..de7e8adbd1 100644 --- a/apps/ospExamples/CMakeLists.txt +++ b/apps/ospExamples/CMakeLists.txt @@ -1,7 +1,7 @@ -## Copyright 2018-2020 Intel Corporation +## Copyright 2018-2021 Intel Corporation ## SPDX-License-Identifier: Apache-2.0 -if (NOT OSPRAY_APPS_EXAMPLES) +if (NOT OSPRAY_ENABLE_APPS_EXAMPLES) return() endif() diff --git a/apps/ospTestSuite/CMakeLists.txt b/apps/ospTestSuite/CMakeLists.txt index db22330ea9..e0a58b2248 100644 --- a/apps/ospTestSuite/CMakeLists.txt +++ b/apps/ospTestSuite/CMakeLists.txt @@ -1,7 +1,7 @@ ## Copyright 2017-2021 Intel Corporation ## SPDX-License-Identifier: Apache-2.0 -if (NOT OSPRAY_APPS_TESTING) +if (NOT OSPRAY_ENABLE_APPS_TESTING) return() endif() @@ -45,7 +45,7 @@ add_executable(ospTestSuite target_link_libraries(ospTestSuite PRIVATE ospray_testing - ospray_module_ispc + ospray_module_cpu ospray_gtest_utils $<$:${GLM_TARGET}> ) diff --git a/apps/ospTutorial/CMakeLists.txt b/apps/ospTutorial/CMakeLists.txt index 15ee0ce572..64ff6d0784 100644 --- a/apps/ospTutorial/CMakeLists.txt +++ b/apps/ospTutorial/CMakeLists.txt @@ -1,7 +1,7 @@ -## Copyright 2018-2020 Intel Corporation +## Copyright 2018-2021 Intel Corporation ## SPDX-License-Identifier: Apache-2.0 -if (NOT OSPRAY_APPS_TUTORIALS) +if (NOT OSPRAY_ENABLE_APPS_TUTORIALS) return() endif() diff --git a/apps/utility/CMakeLists.txt b/apps/utility/CMakeLists.txt index bb9a8398aa..b9c738c195 100644 --- a/apps/utility/CMakeLists.txt +++ b/apps/utility/CMakeLists.txt @@ -1,7 +1,7 @@ -## Copyright 2009-2020 Intel Corporation +## Copyright 2009-2021 Intel Corporation ## SPDX-License-Identifier: Apache-2.0 -if (NOT OSPRAY_APPS_TESTING) +if (NOT OSPRAY_ENABLE_APPS_TESTING) return() endif() diff --git a/cmake/ospray_cmake_config/osprayConfig.cmake.in b/cmake/ospray_cmake_config/osprayConfig.cmake.in index 3623693d6c..8d1a7e18d1 100644 --- a/cmake/ospray_cmake_config/osprayConfig.cmake.in +++ b/cmake/ospray_cmake_config/osprayConfig.cmake.in @@ -1,4 +1,4 @@ -## Copyright 2009-2020 Intel Corporation +## Copyright 2009-2021 Intel Corporation ## SPDX-License-Identifier: Apache-2.0 # Called if we failed to find OSPRay or any of it's required dependencies, @@ -169,7 +169,7 @@ include(${CMAKE_CURRENT_LIST_DIR}/ospray_Exports.cmake) # Add missing targets to libraries set_target_properties(ospray::ospray PROPERTIES INTERFACE_LINK_LIBRARIES "${RKCOMMON_LIBRARY}") -set_target_properties(ospray::ospray_module_ispc PROPERTIES +set_target_properties(ospray::ospray_module_cpu PROPERTIES INTERFACE_LINK_LIBRARIES "ospray::ospray;${OPENVKL_LIBRARY};${EMBREE_LIBRARY}") # Reset CMake module path to its state when this script was called. diff --git a/cmake/ospray_macros.cmake b/cmake/ospray_macros.cmake index 6fcdefe4df..935b53f03f 100644 --- a/cmake/ospray_macros.cmake +++ b/cmake/ospray_macros.cmake @@ -21,13 +21,17 @@ macro(get_subdirectories result curdir) endmacro() ## Get all subdirectories and call add_subdirectory() if it has a CMakeLists.txt -macro(add_all_subdirectories) +macro(add_all_subdirectories_except except) + set(e ${except}) file(GLOB dirs RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/ *) foreach(dir ${dirs}) - if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${dir}/CMakeLists.txt) + if (NOT "${dir}X" STREQUAL "${except}X" AND EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${dir}/CMakeLists.txt) add_subdirectory(${dir}) - endif (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${dir}/CMakeLists.txt) - endforeach(dir ${dirs}) + endif() + endforeach() +endmacro() +macro(add_all_subdirectories) + add_all_subdirectories_except("") endmacro() ## Setup CMAKE_BUILD_TYPE to have a default + cycle between options in UI diff --git a/cmake/ospray_options.cmake b/cmake/ospray_options.cmake index c7cfa34e28..e2dde0fb2b 100644 --- a/cmake/ospray_options.cmake +++ b/cmake/ospray_options.cmake @@ -57,6 +57,39 @@ option(OSPRAY_ENABLE_TARGET_CLANGFORMAT "Enable 'format' target, requires clang-format too") mark_as_advanced(OSPRAY_ENABLE_TARGET_CLANGFORMAT) +# Dependent options +cmake_dependent_option( + OSPRAY_ENABLE_APPS_BENCHMARK + "Enable building, installing, and packaging of benchmark tools." + ON + OSPRAY_ENABLE_APPS + OFF +) + +cmake_dependent_option( + OSPRAY_ENABLE_APPS_EXAMPLES + "Enable building, installing, and packaging of ospExamples." + ON + OSPRAY_ENABLE_APPS + OFF +) + +cmake_dependent_option( + OSPRAY_ENABLE_APPS_TUTORIALS + "Enable building, installing, and packaging of tutorial apps." + ON + OSPRAY_ENABLE_APPS + OFF +) + +cmake_dependent_option( + OSPRAY_ENABLE_APPS_TESTING + "Enable building, installing, and packaging of test tools." + ON + OSPRAY_ENABLE_APPS + OFF +) + ##################################################################### # Binary package options, before any install() invocation/definition ##################################################################### diff --git a/doc/compilation.md b/doc/compilation.md index 468a71052f..3b362c788a 100644 --- a/doc/compilation.md +++ b/doc/compilation.md @@ -260,7 +260,7 @@ Advanced users may want to link to additional targets which are exported in OSPRay's CMake config, which includes all installed modules. All targets built with OSPRay are exported in the `ospray::` namespace, therefore all targets locally used in the OSPRay source tree can be -accessed from an install. For example, `ospray_module_ispc` can be -consumed directly via the `ospray::ospray_module_ispc` target. All +accessed from an install. For example, `ospray_module_cpu` can be +consumed directly via the `ospray::ospray_module_cpu` target. All targets have their libraries, includes, and definitions attached to them for public consumption (please [report bugs] if this is broken!). diff --git a/doc/tutorials.md b/doc/tutorials.md index 19031e6aa9..7423e8047b 100644 --- a/doc/tutorials.md +++ b/doc/tutorials.md @@ -147,7 +147,7 @@ when running cmake with cmake \ -DOSPRAY_ENABLE_APPS=ON \ - -DOSPRAY_APPS_TUTORIALS=ON \ + -DOSPRAY_ENABLE_APPS_TUTORIALS=ON \ -DOSPRAY_MODULE_MPI=ON \ -DOSPRAY_MPI_BUILD_TUTORIALS=ON \ diff --git a/modules/CMakeLists.txt b/modules/CMakeLists.txt index ba801e8569..7bb410de9c 100644 --- a/modules/CMakeLists.txt +++ b/modules/CMakeLists.txt @@ -1,9 +1,12 @@ -## Copyright 2009-2019 Intel Corporation +## Copyright 2009-2021 Intel Corporation ## SPDX-License-Identifier: Apache-2.0 -get_target_property(OSPRAY_ISPC_MODULE_INCLUDE_DIRS ospray_module_ispc - INTERFACE_INCLUDE_DIRECTORIES) +add_subdirectory(cpu) -ispc_include_directories(${OSPRAY_ISPC_MODULE_INCLUDE_DIRS}) +if(OSPRAY_MODULE_CPU) + get_target_property(OSPRAY_CPU_MODULE_INCLUDE_DIRS ospray_module_cpu + INTERFACE_INCLUDE_DIRECTORIES) + ispc_include_directories(${OSPRAY_CPU_MODULE_INCLUDE_DIRS}) +endif() -add_all_subdirectories() +add_all_subdirectories_except(cpu) diff --git a/modules/cpu/CMakeLists.txt b/modules/cpu/CMakeLists.txt new file mode 100644 index 0000000000..302bd4418c --- /dev/null +++ b/modules/cpu/CMakeLists.txt @@ -0,0 +1,328 @@ +## Copyright 2009-2021 Intel Corporation +## SPDX-License-Identifier: Apache-2.0 + +cmake_dependent_option( + OSPRAY_MODULE_CPU + "Build the CPU module" + ON + OSPRAY_ENABLE_MODULES + OFF +) +mark_as_advanced(OSPRAY_MODULE_CPU) + +if (NOT OSPRAY_MODULE_CPU) + return() +endif() + +# ------------------------------------------------------- +# Setup module include directories and source files +# ------------------------------------------------------- + +set(OSPRAY_ISPC_DEVICE_SOURCES + ${OSPRAY_RESOURCE} + + ispc_tasksys.cpp + + ISPCDevice.h + ISPCDevice.cpp + ISPCDevice.ispc + + common/Data.ih + common/Data.cpp + common/Future.cpp + common/Group.cpp + common/Group.ispc + common/Instance.cpp + common/Instance.ispc + common/MotionTransform.cpp + common/Managed.cpp + common/OSPCommon.ispc + common/World.ih + common/World.ispc + common/World.cpp + + fb/FrameBuffer.ispc + fb/FrameBuffer.cpp + fb/FrameBufferView.cpp + fb/LocalFB.ispc + fb/LocalFB.cpp + fb/ImageOp.cpp + fb/Tile.h + fb/TileError.cpp + fb/registration.cpp + + fb/frame_ops/Blur.cpp + fb/frame_ops/Debug.cpp + fb/frame_ops/Depth.cpp + fb/frame_ops/SSAO.cpp + fb/frame_ops/SSAO.ispc + + fb/tile_ops/SaveTiles.cpp + fb/tile_ops/ToneMapper.cpp + fb/tile_ops/ToneMapper.ispc + + pf/PixelFilter.ih + pf/PixelFilter.ispc + pf/LUTPixelFilter.ih + pf/LUTPixelFilter.ispc + pf/PixelFilter.cpp + + camera/Camera.cpp + camera/Camera.ispc + camera/PerspectiveCamera.ispc + camera/PerspectiveCamera.cpp + camera/OrthographicCamera.ispc + camera/OrthographicCamera.cpp + camera/PanoramicCamera.ispc + camera/PanoramicCamera.cpp + camera/registration.cpp + + geometry/Geometry.ispc + geometry/Geometry.cpp + geometry/GeometricModel.ispc + geometry/GeometricModel.cpp + geometry/Boxes.ispc + geometry/Boxes.cpp + geometry/Subdivision.ispc + geometry/Subdivision.cpp + geometry/Curves.cpp + geometry/Curves.ispc + geometry/Spheres.cpp + geometry/Spheres.ispc + geometry/Isosurfaces.ispc + geometry/Isosurfaces.cpp + geometry/Planes.ispc + geometry/Planes.cpp + geometry/Mesh.cpp + geometry/Mesh.ispc + geometry/registration.cpp + + lights/Light.ispc + lights/Light.cpp + lights/AmbientLight.ispc + lights/AmbientLight.cpp + lights/DirectionalLight.cpp + lights/DirectionalLight.ispc + lights/PointLight.cpp + lights/PointLight.ispc + lights/SpotLight.cpp + lights/SpotLight.ispc + lights/QuadLight.cpp + lights/QuadLight.ispc + lights/CylinderLight.cpp + lights/CylinderLight.ispc + lights/HDRILight.cpp + lights/HDRILight.ispc + lights/SunSkyLight.cpp + lights/sky_model/sky_model.cpp + lights/registration.cpp + + math/Distribution1D.ispc + math/Distribution2D.ispc + math/sobol.ih + math/sobol.ispc + math/spectrum.h + math/spectrum.ih + math/spectrum.ispc + math/random.ih + math/halton.ih + math/halton.ispc + + render/LoadBalancer.cpp + render/Material.cpp + render/Renderer.ispc + render/Renderer.cpp + render/util.ispc + render/registration.cpp + + render/debug/DebugRenderer.cpp + render/debug/DebugRenderer.ispc + + render/ao/AORenderer.cpp + render/ao/AORenderer.ispc + render/ao/surfaces.ispc + render/ao/volumes.ispc + + render/scivis/SciVis.cpp + render/scivis/SciVis.ispc + render/scivis/surfaces.ispc + render/scivis/volumes.ispc + render/scivis/lightAlpha.ispc + + render/pathtracer/PathTracer.ispc + render/pathtracer/PathTracer.cpp + render/pathtracer/PathSampler.ispc + render/pathtracer/GeometryLight.ispc + render/pathtracer/VirtualLight.ispc + render/pathtracer/TransparentShadow.ispc + render/pathtracer/ShadowCatcher.ispc + render/pathtracer/NextEventEstimation.ispc + render/pathtracer/volumes/VolumeSampler.ispc + + render/bsdfs/MicrofacetAlbedoTables.ispc + render/materials/Material.ispc + render/materials/OBJ.ispc + render/materials/OBJ.cpp + render/materials/Principled.ispc + render/materials/Principled.cpp + render/materials/CarPaint.ispc + render/materials/CarPaint.cpp + render/materials/Velvet.ispc + render/materials/Velvet.cpp + render/materials/Metal.ispc + render/materials/Metal.cpp + render/materials/Alloy.ispc + render/materials/Alloy.cpp + render/materials/ThinGlass.ispc + render/materials/ThinGlass.cpp + render/materials/Glass.ispc + render/materials/Glass.cpp + render/materials/MetallicPaint.ispc + render/materials/MetallicPaint.cpp + render/materials/Plastic.ispc + render/materials/Plastic.cpp + render/materials/Luminous.ispc + render/materials/Luminous.cpp + render/materials/Mix.ispc + render/materials/Mix.cpp + + texture/Texture.cpp + texture/Texture2D.cpp + texture/Texture2D.ispc + texture/TextureVolume.cpp + texture/TextureVolume.ispc + texture/TextureParam.ispc + texture/registration.cpp + + volume/Volume.ispc + volume/Volume.cpp + volume/VolumetricModel.ispc + volume/VolumetricModel.cpp + + volume/transferFunction/LinearTransferFunction.ispc + volume/transferFunction/LinearTransferFunction.cpp + volume/transferFunction/TransferFunction.ispc + volume/transferFunction/TransferFunction.cpp + volume/transferFunction/registration.cpp +) + +############################################################## +# Build ISPC device +############################################################## + + +## Windows-specifc ISPC symbol exports ## + +if (WIN32) + file(READ def_header.txt OSPRAY_DEF) + file(READ ispc_symbols.txt OSPRAY_ISPC_SYMBOLS_IN) + + foreach(isa ${OSPRAY_ISPC_TARGET_LIST}) + string(REPLACE "-i32x16" "" isa ${isa}) # strip avx512(knl|skx)-i32x16 + # add isa suffix + string(REPLACE "," ${isa} OSPRAY_ISPC_SYMBOLS ${OSPRAY_ISPC_SYMBOLS_IN}) + string(APPEND OSPRAY_DEF ${OSPRAY_ISPC_SYMBOLS}) + endforeach() + + file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/ospray_module_cpu.def.in "${OSPRAY_DEF}") + + # changes .def only if content changed, avoids unnecessary re-linking + configure_file(${CMAKE_CURRENT_BINARY_DIR}/ospray_module_cpu.def.in ospray_module_cpu.def COPYONLY) + + list(APPEND OSPRAY_ISPC_DEVICE_SOURCES ospray_module_cpu.def) +endif() + +## ISPC Device-specific options ## + +set(OSPRAY_TILE_SIZE 64 CACHE STRING "Tile size (x,y dimensions)") +set_property(CACHE OSPRAY_TILE_SIZE PROPERTY STRINGS 8 16 32 64 128 256 512) +mark_as_advanced(OSPRAY_TILE_SIZE) + +if (WIN32) + set(TILE_STACK_DEFAULT_SIZE 64) +elseif (APPLE) + set(TILE_STACK_DEFAULT_SIZE 32) +else () + set(TILE_STACK_DEFAULT_SIZE 128) +endif() + +set(OSPRAY_MAX_STACK_TILE_SIZE ${TILE_STACK_DEFAULT_SIZE} CACHE STRING + "Max size for tile to remain allocated on the stack") +set_property(CACHE OSPRAY_MAX_STACK_TILE_SIZE PROPERTY STRINGS 8 16 32 64 128 256 512) +mark_as_advanced(OSPRAY_MAX_STACK_TILE_SIZE) + +set(OSPRAY_PIXELS_PER_JOB 64 CACHE STRING + "Must be multiple of largest vector width *and* <= OSPRAY_TILE_SIZE") +mark_as_advanced(OSPRAY_PIXELS_PER_JOB) + +option(OSPRAY_DEBUG_NO_TASKING + "Remove tasking parallelism system for debugging" + OFF) +mark_as_advanced(OSPRAY_DEBUG_NO_TASKING) + +configure_file(common/OSPConfig.h.in ${PROJECT_BINARY_DIR}/OSPConfig.h) +install(FILES ${CMAKE_BINARY_DIR}/OSPConfig.h + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/ospray/SDK + COMPONENT devel +) + +## Build the module ## + +ispc_include_directories( + ${PROJECT_SOURCE_DIR}/ospray/include + ${PROJECT_SOURCE_DIR}/ospray + ${PROJECT_SOURCE_DIR} + ${PROJECT_BINARY_DIR} + ${RKCOMMON_INCLUDE_DIRS} + ${EMBREE_INCLUDE_DIRS} + ${OPENVKL_INCLUDE_DIRS} +) +add_library(ospray_module_cpu SHARED) + +ispc_target_add_sources(ospray_module_cpu ${OSPRAY_ISPC_DEVICE_SOURCES}) + +ospray_install_library(ospray_module_cpu lib) + +target_link_libraries(ospray_module_cpu +PUBLIC + ospray + rkcommon::rkcommon + $ + $ +) + +target_include_directories(ospray_module_cpu + PUBLIC + $ + $ + $ + $ + $ + $ + # NOTE(jda) - the following includes are missing despite PUBLIC linking + $ + $ + ####################################################################### + $ + $ +) + +target_compile_definitions(ospray_module_cpu +PUBLIC + TILE_SIZE=${OSPRAY_TILE_SIZE} + MAX_TILE_SIZE=${OSPRAY_MAX_STACK_TILE_SIZE} + RENDERTILE_PIXELS_PER_JOB=${OSPRAY_PIXELS_PER_JOB} + $<$:OSPRAY_SERIAL_RENDERING> +) + + +############################################################## +# Additional interface targets +############################################################## + +add_library(ospray_sdk INTERFACE) +target_link_libraries(ospray_sdk +INTERFACE + ospray_module_cpu rkcommon::rkcommon +) +ospray_install_target(ospray_sdk devel) diff --git a/ospray/api/ISPCDevice.cpp b/modules/cpu/ISPCDevice.cpp similarity index 99% rename from ospray/api/ISPCDevice.cpp rename to modules/cpu/ISPCDevice.cpp index 676cb4a3fa..0ce0e3d9dd 100644 --- a/ospray/api/ISPCDevice.cpp +++ b/modules/cpu/ISPCDevice.cpp @@ -37,7 +37,7 @@ #include "rkcommon/tasking/tasking_system_init.h" #include "rkcommon/utility/CodeTimer.h" -#include "api/ISPCDevice_ispc.h" +#include "ISPCDevice_ispc.h" namespace ospray { namespace api { @@ -559,7 +559,7 @@ OSPPickResult ISPCDevice::pick(OSPFrameBuffer _fb, return renderer->pick(fb, camera, world, screenPos); } -extern "C" OSPError OSPRAY_DLLEXPORT ospray_module_init_ispc( +extern "C" OSPError OSPRAY_DLLEXPORT ospray_module_init_cpu( int16_t versionMajor, int16_t versionMinor, int16_t /*versionPatch*/) { auto status = moduleVersionCheck(versionMajor, versionMinor); diff --git a/ospray/api/ISPCDevice.h b/modules/cpu/ISPCDevice.h similarity index 100% rename from ospray/api/ISPCDevice.h rename to modules/cpu/ISPCDevice.h diff --git a/ospray/api/ISPCDevice.ispc b/modules/cpu/ISPCDevice.ispc similarity index 100% rename from ospray/api/ISPCDevice.ispc rename to modules/cpu/ISPCDevice.ispc diff --git a/ospray/camera/Camera.cpp b/modules/cpu/camera/Camera.cpp similarity index 100% rename from ospray/camera/Camera.cpp rename to modules/cpu/camera/Camera.cpp diff --git a/ospray/camera/Camera.h b/modules/cpu/camera/Camera.h similarity index 100% rename from ospray/camera/Camera.h rename to modules/cpu/camera/Camera.h diff --git a/ospray/camera/Camera.ih b/modules/cpu/camera/Camera.ih similarity index 100% rename from ospray/camera/Camera.ih rename to modules/cpu/camera/Camera.ih diff --git a/ospray/camera/Camera.ispc b/modules/cpu/camera/Camera.ispc similarity index 100% rename from ospray/camera/Camera.ispc rename to modules/cpu/camera/Camera.ispc diff --git a/ospray/camera/OrthographicCamera.cpp b/modules/cpu/camera/OrthographicCamera.cpp similarity index 100% rename from ospray/camera/OrthographicCamera.cpp rename to modules/cpu/camera/OrthographicCamera.cpp diff --git a/ospray/camera/OrthographicCamera.h b/modules/cpu/camera/OrthographicCamera.h similarity index 100% rename from ospray/camera/OrthographicCamera.h rename to modules/cpu/camera/OrthographicCamera.h diff --git a/ospray/camera/OrthographicCamera.ispc b/modules/cpu/camera/OrthographicCamera.ispc similarity index 100% rename from ospray/camera/OrthographicCamera.ispc rename to modules/cpu/camera/OrthographicCamera.ispc diff --git a/ospray/camera/PanoramicCamera.cpp b/modules/cpu/camera/PanoramicCamera.cpp similarity index 100% rename from ospray/camera/PanoramicCamera.cpp rename to modules/cpu/camera/PanoramicCamera.cpp diff --git a/ospray/camera/PanoramicCamera.h b/modules/cpu/camera/PanoramicCamera.h similarity index 100% rename from ospray/camera/PanoramicCamera.h rename to modules/cpu/camera/PanoramicCamera.h diff --git a/ospray/camera/PanoramicCamera.ispc b/modules/cpu/camera/PanoramicCamera.ispc similarity index 100% rename from ospray/camera/PanoramicCamera.ispc rename to modules/cpu/camera/PanoramicCamera.ispc diff --git a/ospray/camera/PerspectiveCamera.cpp b/modules/cpu/camera/PerspectiveCamera.cpp similarity index 100% rename from ospray/camera/PerspectiveCamera.cpp rename to modules/cpu/camera/PerspectiveCamera.cpp diff --git a/ospray/camera/PerspectiveCamera.h b/modules/cpu/camera/PerspectiveCamera.h similarity index 100% rename from ospray/camera/PerspectiveCamera.h rename to modules/cpu/camera/PerspectiveCamera.h diff --git a/ospray/camera/PerspectiveCamera.ispc b/modules/cpu/camera/PerspectiveCamera.ispc similarity index 100% rename from ospray/camera/PerspectiveCamera.ispc rename to modules/cpu/camera/PerspectiveCamera.ispc diff --git a/ospray/camera/registration.cpp b/modules/cpu/camera/registration.cpp similarity index 100% rename from ospray/camera/registration.cpp rename to modules/cpu/camera/registration.cpp diff --git a/ospray/camera/registration.h b/modules/cpu/camera/registration.h similarity index 100% rename from ospray/camera/registration.h rename to modules/cpu/camera/registration.h diff --git a/ospray/common/Clipping.ih b/modules/cpu/common/Clipping.ih similarity index 100% rename from ospray/common/Clipping.ih rename to modules/cpu/common/Clipping.ih diff --git a/ospray/common/Data.cpp b/modules/cpu/common/Data.cpp similarity index 100% rename from ospray/common/Data.cpp rename to modules/cpu/common/Data.cpp diff --git a/ospray/common/Data.h b/modules/cpu/common/Data.h similarity index 100% rename from ospray/common/Data.h rename to modules/cpu/common/Data.h diff --git a/ospray/common/Data.ih b/modules/cpu/common/Data.ih similarity index 100% rename from ospray/common/Data.ih rename to modules/cpu/common/Data.ih diff --git a/ospray/common/DifferentialGeometry.ih b/modules/cpu/common/DifferentialGeometry.ih similarity index 100% rename from ospray/common/DifferentialGeometry.ih rename to modules/cpu/common/DifferentialGeometry.ih diff --git a/ospray/common/FilterIntersect.ih b/modules/cpu/common/FilterIntersect.ih similarity index 100% rename from ospray/common/FilterIntersect.ih rename to modules/cpu/common/FilterIntersect.ih diff --git a/ospray/common/Future.cpp b/modules/cpu/common/Future.cpp similarity index 100% rename from ospray/common/Future.cpp rename to modules/cpu/common/Future.cpp diff --git a/ospray/common/Future.h b/modules/cpu/common/Future.h similarity index 100% rename from ospray/common/Future.h rename to modules/cpu/common/Future.h diff --git a/ospray/common/Group.cpp b/modules/cpu/common/Group.cpp similarity index 100% rename from ospray/common/Group.cpp rename to modules/cpu/common/Group.cpp diff --git a/ospray/common/Group.h b/modules/cpu/common/Group.h similarity index 100% rename from ospray/common/Group.h rename to modules/cpu/common/Group.h diff --git a/ospray/common/Group.ih b/modules/cpu/common/Group.ih similarity index 100% rename from ospray/common/Group.ih rename to modules/cpu/common/Group.ih diff --git a/ospray/common/Group.ispc b/modules/cpu/common/Group.ispc similarity index 100% rename from ospray/common/Group.ispc rename to modules/cpu/common/Group.ispc diff --git a/ospray/common/Instance.cpp b/modules/cpu/common/Instance.cpp similarity index 100% rename from ospray/common/Instance.cpp rename to modules/cpu/common/Instance.cpp diff --git a/ospray/common/Instance.h b/modules/cpu/common/Instance.h similarity index 100% rename from ospray/common/Instance.h rename to modules/cpu/common/Instance.h diff --git a/ospray/common/Instance.ih b/modules/cpu/common/Instance.ih similarity index 100% rename from ospray/common/Instance.ih rename to modules/cpu/common/Instance.ih diff --git a/ospray/common/Instance.ispc b/modules/cpu/common/Instance.ispc similarity index 100% rename from ospray/common/Instance.ispc rename to modules/cpu/common/Instance.ispc diff --git a/ospray/common/Intersect.ih b/modules/cpu/common/Intersect.ih similarity index 100% rename from ospray/common/Intersect.ih rename to modules/cpu/common/Intersect.ih diff --git a/ospray/common/Managed.cpp b/modules/cpu/common/Managed.cpp similarity index 100% rename from ospray/common/Managed.cpp rename to modules/cpu/common/Managed.cpp diff --git a/ospray/common/Managed.h b/modules/cpu/common/Managed.h similarity index 100% rename from ospray/common/Managed.h rename to modules/cpu/common/Managed.h diff --git a/ospray/common/MotionTransform.cpp b/modules/cpu/common/MotionTransform.cpp similarity index 100% rename from ospray/common/MotionTransform.cpp rename to modules/cpu/common/MotionTransform.cpp diff --git a/ospray/common/MotionTransform.h b/modules/cpu/common/MotionTransform.h similarity index 100% rename from ospray/common/MotionTransform.h rename to modules/cpu/common/MotionTransform.h diff --git a/ospray/common/MotionTransform.ih b/modules/cpu/common/MotionTransform.ih similarity index 100% rename from ospray/common/MotionTransform.ih rename to modules/cpu/common/MotionTransform.ih diff --git a/ospray/common/OSPCommon.ih b/modules/cpu/common/OSPCommon.ih similarity index 100% rename from ospray/common/OSPCommon.ih rename to modules/cpu/common/OSPCommon.ih diff --git a/ospray/common/OSPCommon.ispc b/modules/cpu/common/OSPCommon.ispc similarity index 100% rename from ospray/common/OSPCommon.ispc rename to modules/cpu/common/OSPCommon.ispc diff --git a/ospray/common/OSPConfig.h.in b/modules/cpu/common/OSPConfig.h.in similarity index 100% rename from ospray/common/OSPConfig.h.in rename to modules/cpu/common/OSPConfig.h.in diff --git a/ospray/common/Ray.ih b/modules/cpu/common/Ray.ih similarity index 100% rename from ospray/common/Ray.ih rename to modules/cpu/common/Ray.ih diff --git a/ospray/common/VolumeIntervals.ih b/modules/cpu/common/VolumeIntervals.ih similarity index 100% rename from ospray/common/VolumeIntervals.ih rename to modules/cpu/common/VolumeIntervals.ih diff --git a/ospray/common/World.cpp b/modules/cpu/common/World.cpp similarity index 100% rename from ospray/common/World.cpp rename to modules/cpu/common/World.cpp diff --git a/ospray/common/World.h b/modules/cpu/common/World.h similarity index 100% rename from ospray/common/World.h rename to modules/cpu/common/World.h diff --git a/ospray/common/World.ih b/modules/cpu/common/World.ih similarity index 100% rename from ospray/common/World.ih rename to modules/cpu/common/World.ih diff --git a/ospray/common/World.ispc b/modules/cpu/common/World.ispc similarity index 100% rename from ospray/common/World.ispc rename to modules/cpu/common/World.ispc diff --git a/ospray/def_header.txt b/modules/cpu/def_header.txt similarity index 100% rename from ospray/def_header.txt rename to modules/cpu/def_header.txt diff --git a/ospray/fb/FrameBuffer.cpp b/modules/cpu/fb/FrameBuffer.cpp similarity index 100% rename from ospray/fb/FrameBuffer.cpp rename to modules/cpu/fb/FrameBuffer.cpp diff --git a/ospray/fb/FrameBuffer.h b/modules/cpu/fb/FrameBuffer.h similarity index 100% rename from ospray/fb/FrameBuffer.h rename to modules/cpu/fb/FrameBuffer.h diff --git a/ospray/fb/FrameBuffer.ih b/modules/cpu/fb/FrameBuffer.ih similarity index 100% rename from ospray/fb/FrameBuffer.ih rename to modules/cpu/fb/FrameBuffer.ih diff --git a/ospray/fb/FrameBuffer.ispc b/modules/cpu/fb/FrameBuffer.ispc similarity index 100% rename from ospray/fb/FrameBuffer.ispc rename to modules/cpu/fb/FrameBuffer.ispc diff --git a/ospray/fb/FrameBufferView.cpp b/modules/cpu/fb/FrameBufferView.cpp similarity index 100% rename from ospray/fb/FrameBufferView.cpp rename to modules/cpu/fb/FrameBufferView.cpp diff --git a/ospray/fb/FrameBufferView.h b/modules/cpu/fb/FrameBufferView.h similarity index 100% rename from ospray/fb/FrameBufferView.h rename to modules/cpu/fb/FrameBufferView.h diff --git a/ospray/fb/FrameBufferView.ih b/modules/cpu/fb/FrameBufferView.ih similarity index 100% rename from ospray/fb/FrameBufferView.ih rename to modules/cpu/fb/FrameBufferView.ih diff --git a/ospray/fb/ImageOp.cpp b/modules/cpu/fb/ImageOp.cpp similarity index 100% rename from ospray/fb/ImageOp.cpp rename to modules/cpu/fb/ImageOp.cpp diff --git a/ospray/fb/ImageOp.h b/modules/cpu/fb/ImageOp.h similarity index 100% rename from ospray/fb/ImageOp.h rename to modules/cpu/fb/ImageOp.h diff --git a/ospray/fb/LocalFB.cpp b/modules/cpu/fb/LocalFB.cpp similarity index 100% rename from ospray/fb/LocalFB.cpp rename to modules/cpu/fb/LocalFB.cpp diff --git a/ospray/fb/LocalFB.h b/modules/cpu/fb/LocalFB.h similarity index 100% rename from ospray/fb/LocalFB.h rename to modules/cpu/fb/LocalFB.h diff --git a/ospray/fb/LocalFB.ih b/modules/cpu/fb/LocalFB.ih similarity index 100% rename from ospray/fb/LocalFB.ih rename to modules/cpu/fb/LocalFB.ih diff --git a/ospray/fb/LocalFB.ispc b/modules/cpu/fb/LocalFB.ispc similarity index 100% rename from ospray/fb/LocalFB.ispc rename to modules/cpu/fb/LocalFB.ispc diff --git a/ospray/fb/Tile.h b/modules/cpu/fb/Tile.h similarity index 100% rename from ospray/fb/Tile.h rename to modules/cpu/fb/Tile.h diff --git a/ospray/fb/Tile.ih b/modules/cpu/fb/Tile.ih similarity index 100% rename from ospray/fb/Tile.ih rename to modules/cpu/fb/Tile.ih diff --git a/ospray/fb/TileError.cpp b/modules/cpu/fb/TileError.cpp similarity index 100% rename from ospray/fb/TileError.cpp rename to modules/cpu/fb/TileError.cpp diff --git a/ospray/fb/TileError.h b/modules/cpu/fb/TileError.h similarity index 100% rename from ospray/fb/TileError.h rename to modules/cpu/fb/TileError.h diff --git a/ospray/fb/frame_ops/Blur.cpp b/modules/cpu/fb/frame_ops/Blur.cpp similarity index 100% rename from ospray/fb/frame_ops/Blur.cpp rename to modules/cpu/fb/frame_ops/Blur.cpp diff --git a/ospray/fb/frame_ops/Blur.h b/modules/cpu/fb/frame_ops/Blur.h similarity index 100% rename from ospray/fb/frame_ops/Blur.h rename to modules/cpu/fb/frame_ops/Blur.h diff --git a/ospray/fb/frame_ops/Debug.cpp b/modules/cpu/fb/frame_ops/Debug.cpp similarity index 100% rename from ospray/fb/frame_ops/Debug.cpp rename to modules/cpu/fb/frame_ops/Debug.cpp diff --git a/ospray/fb/frame_ops/Debug.h b/modules/cpu/fb/frame_ops/Debug.h similarity index 100% rename from ospray/fb/frame_ops/Debug.h rename to modules/cpu/fb/frame_ops/Debug.h diff --git a/ospray/fb/frame_ops/Depth.cpp b/modules/cpu/fb/frame_ops/Depth.cpp similarity index 100% rename from ospray/fb/frame_ops/Depth.cpp rename to modules/cpu/fb/frame_ops/Depth.cpp diff --git a/ospray/fb/frame_ops/Depth.h b/modules/cpu/fb/frame_ops/Depth.h similarity index 100% rename from ospray/fb/frame_ops/Depth.h rename to modules/cpu/fb/frame_ops/Depth.h diff --git a/ospray/fb/frame_ops/SSAO.cpp b/modules/cpu/fb/frame_ops/SSAO.cpp similarity index 100% rename from ospray/fb/frame_ops/SSAO.cpp rename to modules/cpu/fb/frame_ops/SSAO.cpp diff --git a/ospray/fb/frame_ops/SSAO.h b/modules/cpu/fb/frame_ops/SSAO.h similarity index 100% rename from ospray/fb/frame_ops/SSAO.h rename to modules/cpu/fb/frame_ops/SSAO.h diff --git a/ospray/fb/frame_ops/SSAO.ispc b/modules/cpu/fb/frame_ops/SSAO.ispc similarity index 100% rename from ospray/fb/frame_ops/SSAO.ispc rename to modules/cpu/fb/frame_ops/SSAO.ispc diff --git a/ospray/fb/registration.cpp b/modules/cpu/fb/registration.cpp similarity index 100% rename from ospray/fb/registration.cpp rename to modules/cpu/fb/registration.cpp diff --git a/ospray/fb/registration.h b/modules/cpu/fb/registration.h similarity index 100% rename from ospray/fb/registration.h rename to modules/cpu/fb/registration.h diff --git a/ospray/fb/tile_ops/SaveTiles.cpp b/modules/cpu/fb/tile_ops/SaveTiles.cpp similarity index 100% rename from ospray/fb/tile_ops/SaveTiles.cpp rename to modules/cpu/fb/tile_ops/SaveTiles.cpp diff --git a/ospray/fb/tile_ops/SaveTiles.h b/modules/cpu/fb/tile_ops/SaveTiles.h similarity index 100% rename from ospray/fb/tile_ops/SaveTiles.h rename to modules/cpu/fb/tile_ops/SaveTiles.h diff --git a/ospray/fb/tile_ops/ToneMapper.cpp b/modules/cpu/fb/tile_ops/ToneMapper.cpp similarity index 100% rename from ospray/fb/tile_ops/ToneMapper.cpp rename to modules/cpu/fb/tile_ops/ToneMapper.cpp diff --git a/ospray/fb/tile_ops/ToneMapper.h b/modules/cpu/fb/tile_ops/ToneMapper.h similarity index 100% rename from ospray/fb/tile_ops/ToneMapper.h rename to modules/cpu/fb/tile_ops/ToneMapper.h diff --git a/ospray/fb/tile_ops/ToneMapper.ispc b/modules/cpu/fb/tile_ops/ToneMapper.ispc similarity index 100% rename from ospray/fb/tile_ops/ToneMapper.ispc rename to modules/cpu/fb/tile_ops/ToneMapper.ispc diff --git a/ospray/geometry/Boxes.cpp b/modules/cpu/geometry/Boxes.cpp similarity index 100% rename from ospray/geometry/Boxes.cpp rename to modules/cpu/geometry/Boxes.cpp diff --git a/ospray/geometry/Boxes.h b/modules/cpu/geometry/Boxes.h similarity index 100% rename from ospray/geometry/Boxes.h rename to modules/cpu/geometry/Boxes.h diff --git a/ospray/geometry/Boxes.ispc b/modules/cpu/geometry/Boxes.ispc similarity index 100% rename from ospray/geometry/Boxes.ispc rename to modules/cpu/geometry/Boxes.ispc diff --git a/ospray/geometry/Curves.cpp b/modules/cpu/geometry/Curves.cpp similarity index 100% rename from ospray/geometry/Curves.cpp rename to modules/cpu/geometry/Curves.cpp diff --git a/ospray/geometry/Curves.h b/modules/cpu/geometry/Curves.h similarity index 100% rename from ospray/geometry/Curves.h rename to modules/cpu/geometry/Curves.h diff --git a/ospray/geometry/Curves.ispc b/modules/cpu/geometry/Curves.ispc similarity index 100% rename from ospray/geometry/Curves.ispc rename to modules/cpu/geometry/Curves.ispc diff --git a/ospray/geometry/GeometricModel.cpp b/modules/cpu/geometry/GeometricModel.cpp similarity index 100% rename from ospray/geometry/GeometricModel.cpp rename to modules/cpu/geometry/GeometricModel.cpp diff --git a/ospray/geometry/GeometricModel.h b/modules/cpu/geometry/GeometricModel.h similarity index 100% rename from ospray/geometry/GeometricModel.h rename to modules/cpu/geometry/GeometricModel.h diff --git a/ospray/geometry/GeometricModel.ih b/modules/cpu/geometry/GeometricModel.ih similarity index 100% rename from ospray/geometry/GeometricModel.ih rename to modules/cpu/geometry/GeometricModel.ih diff --git a/ospray/geometry/GeometricModel.ispc b/modules/cpu/geometry/GeometricModel.ispc similarity index 100% rename from ospray/geometry/GeometricModel.ispc rename to modules/cpu/geometry/GeometricModel.ispc diff --git a/ospray/geometry/Geometry.cpp b/modules/cpu/geometry/Geometry.cpp similarity index 100% rename from ospray/geometry/Geometry.cpp rename to modules/cpu/geometry/Geometry.cpp diff --git a/ospray/geometry/Geometry.h b/modules/cpu/geometry/Geometry.h similarity index 100% rename from ospray/geometry/Geometry.h rename to modules/cpu/geometry/Geometry.h diff --git a/ospray/geometry/Geometry.ih b/modules/cpu/geometry/Geometry.ih similarity index 100% rename from ospray/geometry/Geometry.ih rename to modules/cpu/geometry/Geometry.ih diff --git a/ospray/geometry/Geometry.ispc b/modules/cpu/geometry/Geometry.ispc similarity index 100% rename from ospray/geometry/Geometry.ispc rename to modules/cpu/geometry/Geometry.ispc diff --git a/ospray/geometry/Isosurfaces.cpp b/modules/cpu/geometry/Isosurfaces.cpp similarity index 100% rename from ospray/geometry/Isosurfaces.cpp rename to modules/cpu/geometry/Isosurfaces.cpp diff --git a/ospray/geometry/Isosurfaces.h b/modules/cpu/geometry/Isosurfaces.h similarity index 100% rename from ospray/geometry/Isosurfaces.h rename to modules/cpu/geometry/Isosurfaces.h diff --git a/ospray/geometry/Isosurfaces.ispc b/modules/cpu/geometry/Isosurfaces.ispc similarity index 100% rename from ospray/geometry/Isosurfaces.ispc rename to modules/cpu/geometry/Isosurfaces.ispc diff --git a/ospray/geometry/Mesh.cpp b/modules/cpu/geometry/Mesh.cpp similarity index 99% rename from ospray/geometry/Mesh.cpp rename to modules/cpu/geometry/Mesh.cpp index d0ced3a4ce..7c134cb9ee 100644 --- a/ospray/geometry/Mesh.cpp +++ b/modules/cpu/geometry/Mesh.cpp @@ -3,7 +3,7 @@ // ospray #include "Mesh.h" -#include "../include/ospray/ospray.h" +#include "ospray/ospray.h" #include "common/World.h" // ispc exports #include diff --git a/ospray/geometry/Mesh.h b/modules/cpu/geometry/Mesh.h similarity index 100% rename from ospray/geometry/Mesh.h rename to modules/cpu/geometry/Mesh.h diff --git a/ospray/geometry/Mesh.ispc b/modules/cpu/geometry/Mesh.ispc similarity index 100% rename from ospray/geometry/Mesh.ispc rename to modules/cpu/geometry/Mesh.ispc diff --git a/ospray/geometry/Planes.cpp b/modules/cpu/geometry/Planes.cpp similarity index 100% rename from ospray/geometry/Planes.cpp rename to modules/cpu/geometry/Planes.cpp diff --git a/ospray/geometry/Planes.h b/modules/cpu/geometry/Planes.h similarity index 100% rename from ospray/geometry/Planes.h rename to modules/cpu/geometry/Planes.h diff --git a/ospray/geometry/Planes.ispc b/modules/cpu/geometry/Planes.ispc similarity index 100% rename from ospray/geometry/Planes.ispc rename to modules/cpu/geometry/Planes.ispc diff --git a/ospray/geometry/Spheres.cpp b/modules/cpu/geometry/Spheres.cpp similarity index 100% rename from ospray/geometry/Spheres.cpp rename to modules/cpu/geometry/Spheres.cpp diff --git a/ospray/geometry/Spheres.h b/modules/cpu/geometry/Spheres.h similarity index 100% rename from ospray/geometry/Spheres.h rename to modules/cpu/geometry/Spheres.h diff --git a/ospray/geometry/Spheres.ispc b/modules/cpu/geometry/Spheres.ispc similarity index 100% rename from ospray/geometry/Spheres.ispc rename to modules/cpu/geometry/Spheres.ispc diff --git a/ospray/geometry/Subdivision.cpp b/modules/cpu/geometry/Subdivision.cpp similarity index 99% rename from ospray/geometry/Subdivision.cpp rename to modules/cpu/geometry/Subdivision.cpp index 46a9ec114e..3784a0be02 100644 --- a/ospray/geometry/Subdivision.cpp +++ b/modules/cpu/geometry/Subdivision.cpp @@ -3,7 +3,7 @@ // ospray #include "Subdivision.h" -#include "../include/ospray/ospray.h" +#include "ospray/ospray.h" #include "common/World.h" // ispc exports #include diff --git a/ospray/geometry/Subdivision.h b/modules/cpu/geometry/Subdivision.h similarity index 100% rename from ospray/geometry/Subdivision.h rename to modules/cpu/geometry/Subdivision.h diff --git a/ospray/geometry/Subdivision.ispc b/modules/cpu/geometry/Subdivision.ispc similarity index 100% rename from ospray/geometry/Subdivision.ispc rename to modules/cpu/geometry/Subdivision.ispc diff --git a/ospray/geometry/registration.cpp b/modules/cpu/geometry/registration.cpp similarity index 100% rename from ospray/geometry/registration.cpp rename to modules/cpu/geometry/registration.cpp diff --git a/ospray/geometry/registration.h b/modules/cpu/geometry/registration.h similarity index 100% rename from ospray/geometry/registration.h rename to modules/cpu/geometry/registration.h diff --git a/ospray/ispc_symbols.txt b/modules/cpu/ispc_symbols.txt similarity index 98% rename from ospray/ispc_symbols.txt rename to modules/cpu/ispc_symbols.txt index 4355965eb0..0017fb0d96 100644 --- a/ospray/ispc_symbols.txt +++ b/modules/cpu/ispc_symbols.txt @@ -1,18 +1,18 @@ -Distribution2D_create___s_5B__c_unvec2i_5D_un_3C_unf_3E_, -Distribution2D_destroy___un_3C_s_5B_unDistribution2D_5D__3E_, -Distribution2D_pdf___un_3C_s_5B__c_unDistribution2D_5D__3E_REFs_5B__c_vyvec2f_5D_, -Distribution2D_sample___un_3C_s_5B__c_unDistribution2D_5D__3E_REFs_5B__c_vyvec2f_5D_, -FrameBuffer_Constructor___un_3C_s_5B_unFrameBuffer_5D__3E_, -FrameBuffer_set___un_3C_s_5B_unFrameBuffer_5D__3E_CunuCunuuni, -Geometry_Constructor___un_3C_s_5B_unGeometry_5D__3E_un_3C____un_3C_s_5B__c_unGeometry_5D__3E_REFs_5B_vyDifferentialGeometry_5D_REFs_5B__c_vyRay_5D_unI_3E_, -Light_eval___un_3C_s_5B__c_unLight_5D__3E_REFs_5B__c_vyDifferentialGeometry_5D_REFs_5B__c_vyvec3f_5D_CvyfCvyfCvyf, -Renderer_Constructor___un_3C_s_5B_unRenderer_5D__3E_, -Renderer_getBackground___un_3C_s_5B__c_unRenderer_5D__3E_REFs_5B__c_vyvec2f_5D_, -Renderer_getMaxDepth___un_3C_s_5B__c_unRenderer_5D__3E_REFs_5B__c_vyvec2f_5D_, -Volume_Constructor___un_3C_s_5B_unVolume_5D__3E_, -World_Constructor___un_3C_s_5B_unWorld_5D__3E_, -clippingIntersectionFilterV___UM_un_3C_s_5B__c_unRTCFilterFunctionNArguments_5D__3E_, -delete_uniform___un_3C_unv_3E_, -delete_uniform_, -precomputedZOrder_create___, -get_zorder___, +Distribution2D_create___s_5B__c_unvec2i_5D_un_3C_unf_3E_, +Distribution2D_destroy___un_3C_s_5B_unDistribution2D_5D__3E_, +Distribution2D_pdf___un_3C_s_5B__c_unDistribution2D_5D__3E_REFs_5B__c_vyvec2f_5D_, +Distribution2D_sample___un_3C_s_5B__c_unDistribution2D_5D__3E_REFs_5B__c_vyvec2f_5D_, +FrameBuffer_Constructor___un_3C_s_5B_unFrameBuffer_5D__3E_, +FrameBuffer_set___un_3C_s_5B_unFrameBuffer_5D__3E_CunuCunuuni, +Geometry_Constructor___un_3C_s_5B_unGeometry_5D__3E_un_3C____un_3C_s_5B__c_unGeometry_5D__3E_REFs_5B_vyDifferentialGeometry_5D_REFs_5B__c_vyRay_5D_unI_3E_, +Light_eval___un_3C_s_5B__c_unLight_5D__3E_REFs_5B__c_vyDifferentialGeometry_5D_REFs_5B__c_vyvec3f_5D_CvyfCvyfCvyf, +Renderer_Constructor___un_3C_s_5B_unRenderer_5D__3E_, +Renderer_getBackground___un_3C_s_5B__c_unRenderer_5D__3E_REFs_5B__c_vyvec2f_5D_, +Renderer_getMaxDepth___un_3C_s_5B__c_unRenderer_5D__3E_REFs_5B__c_vyvec2f_5D_, +Volume_Constructor___un_3C_s_5B_unVolume_5D__3E_, +World_Constructor___un_3C_s_5B_unWorld_5D__3E_, +clippingIntersectionFilterV___UM_un_3C_s_5B__c_unRTCFilterFunctionNArguments_5D__3E_, +delete_uniform___un_3C_unv_3E_, +delete_uniform_, +precomputedZOrder_create___, +get_zorder___, diff --git a/ospray/ispc_tasksys.cpp b/modules/cpu/ispc_tasksys.cpp similarity index 100% rename from ospray/ispc_tasksys.cpp rename to modules/cpu/ispc_tasksys.cpp diff --git a/ospray/lights/AmbientLight.cpp b/modules/cpu/lights/AmbientLight.cpp similarity index 100% rename from ospray/lights/AmbientLight.cpp rename to modules/cpu/lights/AmbientLight.cpp diff --git a/ospray/lights/AmbientLight.h b/modules/cpu/lights/AmbientLight.h similarity index 100% rename from ospray/lights/AmbientLight.h rename to modules/cpu/lights/AmbientLight.h diff --git a/ospray/lights/AmbientLight.ispc b/modules/cpu/lights/AmbientLight.ispc similarity index 100% rename from ospray/lights/AmbientLight.ispc rename to modules/cpu/lights/AmbientLight.ispc diff --git a/ospray/lights/CylinderCapSolidAngleTable.ih b/modules/cpu/lights/CylinderCapSolidAngleTable.ih similarity index 100% rename from ospray/lights/CylinderCapSolidAngleTable.ih rename to modules/cpu/lights/CylinderCapSolidAngleTable.ih diff --git a/ospray/lights/CylinderLight.cpp b/modules/cpu/lights/CylinderLight.cpp similarity index 100% rename from ospray/lights/CylinderLight.cpp rename to modules/cpu/lights/CylinderLight.cpp diff --git a/ospray/lights/CylinderLight.h b/modules/cpu/lights/CylinderLight.h similarity index 100% rename from ospray/lights/CylinderLight.h rename to modules/cpu/lights/CylinderLight.h diff --git a/ospray/lights/CylinderLight.ispc b/modules/cpu/lights/CylinderLight.ispc similarity index 100% rename from ospray/lights/CylinderLight.ispc rename to modules/cpu/lights/CylinderLight.ispc diff --git a/ospray/lights/DirectionalLight.cpp b/modules/cpu/lights/DirectionalLight.cpp similarity index 100% rename from ospray/lights/DirectionalLight.cpp rename to modules/cpu/lights/DirectionalLight.cpp diff --git a/ospray/lights/DirectionalLight.h b/modules/cpu/lights/DirectionalLight.h similarity index 100% rename from ospray/lights/DirectionalLight.h rename to modules/cpu/lights/DirectionalLight.h diff --git a/ospray/lights/DirectionalLight.ispc b/modules/cpu/lights/DirectionalLight.ispc similarity index 100% rename from ospray/lights/DirectionalLight.ispc rename to modules/cpu/lights/DirectionalLight.ispc diff --git a/ospray/lights/HDRILight.cpp b/modules/cpu/lights/HDRILight.cpp similarity index 100% rename from ospray/lights/HDRILight.cpp rename to modules/cpu/lights/HDRILight.cpp diff --git a/ospray/lights/HDRILight.h b/modules/cpu/lights/HDRILight.h similarity index 100% rename from ospray/lights/HDRILight.h rename to modules/cpu/lights/HDRILight.h diff --git a/ospray/lights/HDRILight.ispc b/modules/cpu/lights/HDRILight.ispc similarity index 100% rename from ospray/lights/HDRILight.ispc rename to modules/cpu/lights/HDRILight.ispc diff --git a/ospray/lights/Light.cpp b/modules/cpu/lights/Light.cpp similarity index 100% rename from ospray/lights/Light.cpp rename to modules/cpu/lights/Light.cpp diff --git a/ospray/lights/Light.h b/modules/cpu/lights/Light.h similarity index 100% rename from ospray/lights/Light.h rename to modules/cpu/lights/Light.h diff --git a/ospray/lights/Light.ih b/modules/cpu/lights/Light.ih similarity index 100% rename from ospray/lights/Light.ih rename to modules/cpu/lights/Light.ih diff --git a/ospray/lights/Light.ispc b/modules/cpu/lights/Light.ispc similarity index 100% rename from ospray/lights/Light.ispc rename to modules/cpu/lights/Light.ispc diff --git a/ospray/lights/PointLight.cpp b/modules/cpu/lights/PointLight.cpp similarity index 100% rename from ospray/lights/PointLight.cpp rename to modules/cpu/lights/PointLight.cpp diff --git a/ospray/lights/PointLight.h b/modules/cpu/lights/PointLight.h similarity index 100% rename from ospray/lights/PointLight.h rename to modules/cpu/lights/PointLight.h diff --git a/ospray/lights/PointLight.ispc b/modules/cpu/lights/PointLight.ispc similarity index 100% rename from ospray/lights/PointLight.ispc rename to modules/cpu/lights/PointLight.ispc diff --git a/ospray/lights/QuadLight.cpp b/modules/cpu/lights/QuadLight.cpp similarity index 100% rename from ospray/lights/QuadLight.cpp rename to modules/cpu/lights/QuadLight.cpp diff --git a/ospray/lights/QuadLight.h b/modules/cpu/lights/QuadLight.h similarity index 100% rename from ospray/lights/QuadLight.h rename to modules/cpu/lights/QuadLight.h diff --git a/ospray/lights/QuadLight.ispc b/modules/cpu/lights/QuadLight.ispc similarity index 100% rename from ospray/lights/QuadLight.ispc rename to modules/cpu/lights/QuadLight.ispc diff --git a/ospray/lights/SphericalQuadSampling.ih b/modules/cpu/lights/SphericalQuadSampling.ih similarity index 97% rename from ospray/lights/SphericalQuadSampling.ih rename to modules/cpu/lights/SphericalQuadSampling.ih index b5720a5dd5..2fe14d0df7 100644 --- a/ospray/lights/SphericalQuadSampling.ih +++ b/modules/cpu/lights/SphericalQuadSampling.ih @@ -28,7 +28,7 @@ SphericalQuad SphericalQuad_create(const vec3f &q0, vec3f d = q0 - P; quad.z0 = dot(d, quad.z); - // flip ā€™zā€™ to make it point against ā€™Qā€™ + // flip z to make it point against Q quad.z = quad.z0 > 0.f ? neg(quad.z) : quad.z; quad.z0 = -abs(quad.z0); @@ -78,18 +78,18 @@ inline float sign(float x) vec3f sampleSphericalQuad(const SphericalQuad &quad, const vec2f &s) { - // 1. compute ā€™cuā€™ + // 1. compute cu const float au = s.x * quad.S + quad.k; float cosau, sinau; sincos(au, &sinau, &cosau); const float fu = (cosau * quad.n0z - quad.n2z) * rcp(sinau); const float cu = clamp(rsqrt(fu * fu + sqr(quad.n0z)) * sign(fu), -1.f, 1.f); - // 2. compute ā€™xuā€™ + // 2. compute xu const float x0 = -(cu * quad.z0) * rsqrt(1.f - cu * cu); const float xu = clamp(x0, quad.x0, quad.x1); - // 3. compute ā€™yvā€™ + // 3. compute yv const float ds = sqrt(sqr(xu) + sqr(quad.z0)); const float ds2 = sqr(ds); const float h0 = quad.y0 * rsqrt(ds2 + sqr(quad.y0)); diff --git a/ospray/lights/SpotLight.cpp b/modules/cpu/lights/SpotLight.cpp similarity index 100% rename from ospray/lights/SpotLight.cpp rename to modules/cpu/lights/SpotLight.cpp diff --git a/ospray/lights/SpotLight.h b/modules/cpu/lights/SpotLight.h similarity index 100% rename from ospray/lights/SpotLight.h rename to modules/cpu/lights/SpotLight.h diff --git a/ospray/lights/SpotLight.ispc b/modules/cpu/lights/SpotLight.ispc similarity index 100% rename from ospray/lights/SpotLight.ispc rename to modules/cpu/lights/SpotLight.ispc diff --git a/ospray/lights/SunSkyLight.cpp b/modules/cpu/lights/SunSkyLight.cpp similarity index 100% rename from ospray/lights/SunSkyLight.cpp rename to modules/cpu/lights/SunSkyLight.cpp diff --git a/ospray/lights/SunSkyLight.h b/modules/cpu/lights/SunSkyLight.h similarity index 100% rename from ospray/lights/SunSkyLight.h rename to modules/cpu/lights/SunSkyLight.h diff --git a/ospray/lights/registration.cpp b/modules/cpu/lights/registration.cpp similarity index 100% rename from ospray/lights/registration.cpp rename to modules/cpu/lights/registration.cpp diff --git a/ospray/lights/registration.h b/modules/cpu/lights/registration.h similarity index 100% rename from ospray/lights/registration.h rename to modules/cpu/lights/registration.h diff --git a/ospray/lights/sky_model/color_info.h b/modules/cpu/lights/sky_model/color_info.h similarity index 100% rename from ospray/lights/sky_model/color_info.h rename to modules/cpu/lights/sky_model/color_info.h diff --git a/ospray/lights/sky_model/sky_model.cpp b/modules/cpu/lights/sky_model/sky_model.cpp similarity index 100% rename from ospray/lights/sky_model/sky_model.cpp rename to modules/cpu/lights/sky_model/sky_model.cpp diff --git a/ospray/lights/sky_model/sky_model.h b/modules/cpu/lights/sky_model/sky_model.h similarity index 100% rename from ospray/lights/sky_model/sky_model.h rename to modules/cpu/lights/sky_model/sky_model.h diff --git a/ospray/lights/sky_model/sky_model_data_ciexyz.h b/modules/cpu/lights/sky_model/sky_model_data_ciexyz.h similarity index 100% rename from ospray/lights/sky_model/sky_model_data_ciexyz.h rename to modules/cpu/lights/sky_model/sky_model_data_ciexyz.h diff --git a/ospray/lights/sky_model/sky_model_data_rgb.h b/modules/cpu/lights/sky_model/sky_model_data_rgb.h similarity index 100% rename from ospray/lights/sky_model/sky_model_data_rgb.h rename to modules/cpu/lights/sky_model/sky_model_data_rgb.h diff --git a/ospray/lights/sky_model/sky_model_data_spectral.h b/modules/cpu/lights/sky_model/sky_model_data_spectral.h similarity index 100% rename from ospray/lights/sky_model/sky_model_data_spectral.h rename to modules/cpu/lights/sky_model/sky_model_data_spectral.h diff --git a/ospray/math/AffineSpace.ih b/modules/cpu/math/AffineSpace.ih similarity index 100% rename from ospray/math/AffineSpace.ih rename to modules/cpu/math/AffineSpace.ih diff --git a/ospray/math/Distribution1D.ih b/modules/cpu/math/Distribution1D.ih similarity index 100% rename from ospray/math/Distribution1D.ih rename to modules/cpu/math/Distribution1D.ih diff --git a/ospray/math/Distribution1D.ispc b/modules/cpu/math/Distribution1D.ispc similarity index 100% rename from ospray/math/Distribution1D.ispc rename to modules/cpu/math/Distribution1D.ispc diff --git a/ospray/math/Distribution2D.ih b/modules/cpu/math/Distribution2D.ih similarity index 100% rename from ospray/math/Distribution2D.ih rename to modules/cpu/math/Distribution2D.ih diff --git a/ospray/math/Distribution2D.ispc b/modules/cpu/math/Distribution2D.ispc similarity index 100% rename from ospray/math/Distribution2D.ispc rename to modules/cpu/math/Distribution2D.ispc diff --git a/ospray/math/LinearSpace.ih b/modules/cpu/math/LinearSpace.ih similarity index 100% rename from ospray/math/LinearSpace.ih rename to modules/cpu/math/LinearSpace.ih diff --git a/ospray/math/halton.ih b/modules/cpu/math/halton.ih similarity index 100% rename from ospray/math/halton.ih rename to modules/cpu/math/halton.ih diff --git a/ospray/math/halton.ispc b/modules/cpu/math/halton.ispc similarity index 100% rename from ospray/math/halton.ispc rename to modules/cpu/math/halton.ispc diff --git a/ospray/math/interpolation.ih b/modules/cpu/math/interpolation.ih similarity index 100% rename from ospray/math/interpolation.ih rename to modules/cpu/math/interpolation.ih diff --git a/ospray/math/random.ih b/modules/cpu/math/random.ih similarity index 100% rename from ospray/math/random.ih rename to modules/cpu/math/random.ih diff --git a/ospray/math/sampling.ih b/modules/cpu/math/sampling.ih similarity index 100% rename from ospray/math/sampling.ih rename to modules/cpu/math/sampling.ih diff --git a/ospray/math/sobol.ih b/modules/cpu/math/sobol.ih similarity index 100% rename from ospray/math/sobol.ih rename to modules/cpu/math/sobol.ih diff --git a/ospray/math/sobol.ispc b/modules/cpu/math/sobol.ispc similarity index 100% rename from ospray/math/sobol.ispc rename to modules/cpu/math/sobol.ispc diff --git a/ospray/math/spectrum.h b/modules/cpu/math/spectrum.h similarity index 100% rename from ospray/math/spectrum.h rename to modules/cpu/math/spectrum.h diff --git a/ospray/math/spectrum.ih b/modules/cpu/math/spectrum.ih similarity index 100% rename from ospray/math/spectrum.ih rename to modules/cpu/math/spectrum.ih diff --git a/ospray/math/spectrum.ispc b/modules/cpu/math/spectrum.ispc similarity index 100% rename from ospray/math/spectrum.ispc rename to modules/cpu/math/spectrum.ispc diff --git a/ospray/pf/LUTPixelFilter.ih b/modules/cpu/pf/LUTPixelFilter.ih similarity index 100% rename from ospray/pf/LUTPixelFilter.ih rename to modules/cpu/pf/LUTPixelFilter.ih diff --git a/ospray/pf/LUTPixelFilter.ispc b/modules/cpu/pf/LUTPixelFilter.ispc similarity index 100% rename from ospray/pf/LUTPixelFilter.ispc rename to modules/cpu/pf/LUTPixelFilter.ispc diff --git a/ospray/pf/PixelFilter.cpp b/modules/cpu/pf/PixelFilter.cpp similarity index 100% rename from ospray/pf/PixelFilter.cpp rename to modules/cpu/pf/PixelFilter.cpp diff --git a/ospray/pf/PixelFilter.h b/modules/cpu/pf/PixelFilter.h similarity index 100% rename from ospray/pf/PixelFilter.h rename to modules/cpu/pf/PixelFilter.h diff --git a/ospray/pf/PixelFilter.ih b/modules/cpu/pf/PixelFilter.ih similarity index 100% rename from ospray/pf/PixelFilter.ih rename to modules/cpu/pf/PixelFilter.ih diff --git a/ospray/pf/PixelFilter.ispc b/modules/cpu/pf/PixelFilter.ispc similarity index 100% rename from ospray/pf/PixelFilter.ispc rename to modules/cpu/pf/PixelFilter.ispc diff --git a/ospray/render/LoadBalancer.cpp b/modules/cpu/render/LoadBalancer.cpp similarity index 100% rename from ospray/render/LoadBalancer.cpp rename to modules/cpu/render/LoadBalancer.cpp diff --git a/ospray/render/LoadBalancer.h b/modules/cpu/render/LoadBalancer.h similarity index 100% rename from ospray/render/LoadBalancer.h rename to modules/cpu/render/LoadBalancer.h diff --git a/ospray/render/Material.cpp b/modules/cpu/render/Material.cpp similarity index 100% rename from ospray/render/Material.cpp rename to modules/cpu/render/Material.cpp diff --git a/ospray/render/Material.h b/modules/cpu/render/Material.h similarity index 100% rename from ospray/render/Material.h rename to modules/cpu/render/Material.h diff --git a/ospray/render/Material.ih b/modules/cpu/render/Material.ih similarity index 100% rename from ospray/render/Material.ih rename to modules/cpu/render/Material.ih diff --git a/ospray/render/MaterialType.ih b/modules/cpu/render/MaterialType.ih similarity index 100% rename from ospray/render/MaterialType.ih rename to modules/cpu/render/MaterialType.ih diff --git a/ospray/render/RenderTask.h b/modules/cpu/render/RenderTask.h similarity index 100% rename from ospray/render/RenderTask.h rename to modules/cpu/render/RenderTask.h diff --git a/ospray/render/Renderer.cpp b/modules/cpu/render/Renderer.cpp similarity index 100% rename from ospray/render/Renderer.cpp rename to modules/cpu/render/Renderer.cpp diff --git a/ospray/render/Renderer.h b/modules/cpu/render/Renderer.h similarity index 100% rename from ospray/render/Renderer.h rename to modules/cpu/render/Renderer.h diff --git a/ospray/render/Renderer.ih b/modules/cpu/render/Renderer.ih similarity index 100% rename from ospray/render/Renderer.ih rename to modules/cpu/render/Renderer.ih diff --git a/ospray/render/Renderer.ispc b/modules/cpu/render/Renderer.ispc similarity index 100% rename from ospray/render/Renderer.ispc rename to modules/cpu/render/Renderer.ispc diff --git a/ospray/render/ao/AORenderer.cpp b/modules/cpu/render/ao/AORenderer.cpp similarity index 100% rename from ospray/render/ao/AORenderer.cpp rename to modules/cpu/render/ao/AORenderer.cpp diff --git a/ospray/render/ao/AORenderer.h b/modules/cpu/render/ao/AORenderer.h similarity index 100% rename from ospray/render/ao/AORenderer.h rename to modules/cpu/render/ao/AORenderer.h diff --git a/ospray/render/ao/AORenderer.ih b/modules/cpu/render/ao/AORenderer.ih similarity index 100% rename from ospray/render/ao/AORenderer.ih rename to modules/cpu/render/ao/AORenderer.ih diff --git a/ospray/render/ao/AORenderer.ispc b/modules/cpu/render/ao/AORenderer.ispc similarity index 100% rename from ospray/render/ao/AORenderer.ispc rename to modules/cpu/render/ao/AORenderer.ispc diff --git a/ospray/render/ao/surfaces.ih b/modules/cpu/render/ao/surfaces.ih similarity index 100% rename from ospray/render/ao/surfaces.ih rename to modules/cpu/render/ao/surfaces.ih diff --git a/ospray/render/ao/surfaces.ispc b/modules/cpu/render/ao/surfaces.ispc similarity index 100% rename from ospray/render/ao/surfaces.ispc rename to modules/cpu/render/ao/surfaces.ispc diff --git a/ospray/render/ao/volumes.ih b/modules/cpu/render/ao/volumes.ih similarity index 100% rename from ospray/render/ao/volumes.ih rename to modules/cpu/render/ao/volumes.ih diff --git a/ospray/render/ao/volumes.ispc b/modules/cpu/render/ao/volumes.ispc similarity index 100% rename from ospray/render/ao/volumes.ispc rename to modules/cpu/render/ao/volumes.ispc diff --git a/ospray/render/bsdfs/BSDF.ih b/modules/cpu/render/bsdfs/BSDF.ih similarity index 100% rename from ospray/render/bsdfs/BSDF.ih rename to modules/cpu/render/bsdfs/BSDF.ih diff --git a/ospray/render/bsdfs/BeckmannDistribution.ih b/modules/cpu/render/bsdfs/BeckmannDistribution.ih similarity index 100% rename from ospray/render/bsdfs/BeckmannDistribution.ih rename to modules/cpu/render/bsdfs/BeckmannDistribution.ih diff --git a/ospray/render/bsdfs/Conductor.ih b/modules/cpu/render/bsdfs/Conductor.ih similarity index 100% rename from ospray/render/bsdfs/Conductor.ih rename to modules/cpu/render/bsdfs/Conductor.ih diff --git a/ospray/render/bsdfs/Dielectric.ih b/modules/cpu/render/bsdfs/Dielectric.ih similarity index 100% rename from ospray/render/bsdfs/Dielectric.ih rename to modules/cpu/render/bsdfs/Dielectric.ih diff --git a/ospray/render/bsdfs/DielectricLayer.ih b/modules/cpu/render/bsdfs/DielectricLayer.ih similarity index 100% rename from ospray/render/bsdfs/DielectricLayer.ih rename to modules/cpu/render/bsdfs/DielectricLayer.ih diff --git a/ospray/render/bsdfs/Fresnel.ih b/modules/cpu/render/bsdfs/Fresnel.ih similarity index 100% rename from ospray/render/bsdfs/Fresnel.ih rename to modules/cpu/render/bsdfs/Fresnel.ih diff --git a/ospray/render/bsdfs/GGXDistribution.ih b/modules/cpu/render/bsdfs/GGXDistribution.ih similarity index 100% rename from ospray/render/bsdfs/GGXDistribution.ih rename to modules/cpu/render/bsdfs/GGXDistribution.ih diff --git a/ospray/render/bsdfs/Lambert.ih b/modules/cpu/render/bsdfs/Lambert.ih similarity index 100% rename from ospray/render/bsdfs/Lambert.ih rename to modules/cpu/render/bsdfs/Lambert.ih diff --git a/ospray/render/bsdfs/LambertTransmission.ih b/modules/cpu/render/bsdfs/LambertTransmission.ih similarity index 100% rename from ospray/render/bsdfs/LambertTransmission.ih rename to modules/cpu/render/bsdfs/LambertTransmission.ih diff --git a/ospray/render/bsdfs/MicrofacetAlbedo.ih b/modules/cpu/render/bsdfs/MicrofacetAlbedo.ih similarity index 100% rename from ospray/render/bsdfs/MicrofacetAlbedo.ih rename to modules/cpu/render/bsdfs/MicrofacetAlbedo.ih diff --git a/ospray/render/bsdfs/MicrofacetAlbedoTables.ih b/modules/cpu/render/bsdfs/MicrofacetAlbedoTables.ih similarity index 100% rename from ospray/render/bsdfs/MicrofacetAlbedoTables.ih rename to modules/cpu/render/bsdfs/MicrofacetAlbedoTables.ih diff --git a/ospray/render/bsdfs/MicrofacetAlbedoTables.ispc b/modules/cpu/render/bsdfs/MicrofacetAlbedoTables.ispc similarity index 100% rename from ospray/render/bsdfs/MicrofacetAlbedoTables.ispc rename to modules/cpu/render/bsdfs/MicrofacetAlbedoTables.ispc diff --git a/ospray/render/bsdfs/MicrofacetConductor.ih b/modules/cpu/render/bsdfs/MicrofacetConductor.ih similarity index 100% rename from ospray/render/bsdfs/MicrofacetConductor.ih rename to modules/cpu/render/bsdfs/MicrofacetConductor.ih diff --git a/ospray/render/bsdfs/MicrofacetDielectric.ih b/modules/cpu/render/bsdfs/MicrofacetDielectric.ih similarity index 100% rename from ospray/render/bsdfs/MicrofacetDielectric.ih rename to modules/cpu/render/bsdfs/MicrofacetDielectric.ih diff --git a/ospray/render/bsdfs/MicrofacetDielectricLayer.ih b/modules/cpu/render/bsdfs/MicrofacetDielectricLayer.ih similarity index 100% rename from ospray/render/bsdfs/MicrofacetDielectricLayer.ih rename to modules/cpu/render/bsdfs/MicrofacetDielectricLayer.ih diff --git a/ospray/render/bsdfs/MicrofacetDistribution.ih b/modules/cpu/render/bsdfs/MicrofacetDistribution.ih similarity index 100% rename from ospray/render/bsdfs/MicrofacetDistribution.ih rename to modules/cpu/render/bsdfs/MicrofacetDistribution.ih diff --git a/ospray/render/bsdfs/MicrofacetSheenLayer.ih b/modules/cpu/render/bsdfs/MicrofacetSheenLayer.ih similarity index 100% rename from ospray/render/bsdfs/MicrofacetSheenLayer.ih rename to modules/cpu/render/bsdfs/MicrofacetSheenLayer.ih diff --git a/ospray/render/bsdfs/Minneart.ih b/modules/cpu/render/bsdfs/Minneart.ih similarity index 100% rename from ospray/render/bsdfs/Minneart.ih rename to modules/cpu/render/bsdfs/Minneart.ih diff --git a/ospray/render/bsdfs/MultiBSDF.ih b/modules/cpu/render/bsdfs/MultiBSDF.ih similarity index 100% rename from ospray/render/bsdfs/MultiBSDF.ih rename to modules/cpu/render/bsdfs/MultiBSDF.ih diff --git a/ospray/render/bsdfs/Optics.ih b/modules/cpu/render/bsdfs/Optics.ih similarity index 100% rename from ospray/render/bsdfs/Optics.ih rename to modules/cpu/render/bsdfs/Optics.ih diff --git a/ospray/render/bsdfs/OrenNayar.ih b/modules/cpu/render/bsdfs/OrenNayar.ih similarity index 100% rename from ospray/render/bsdfs/OrenNayar.ih rename to modules/cpu/render/bsdfs/OrenNayar.ih diff --git a/ospray/render/bsdfs/PowerCosineDistribution.ih b/modules/cpu/render/bsdfs/PowerCosineDistribution.ih similarity index 100% rename from ospray/render/bsdfs/PowerCosineDistribution.ih rename to modules/cpu/render/bsdfs/PowerCosineDistribution.ih diff --git a/ospray/render/bsdfs/Reflection.ih b/modules/cpu/render/bsdfs/Reflection.ih similarity index 100% rename from ospray/render/bsdfs/Reflection.ih rename to modules/cpu/render/bsdfs/Reflection.ih diff --git a/ospray/render/bsdfs/RobustDielectric.ih b/modules/cpu/render/bsdfs/RobustDielectric.ih similarity index 100% rename from ospray/render/bsdfs/RobustDielectric.ih rename to modules/cpu/render/bsdfs/RobustDielectric.ih diff --git a/ospray/render/bsdfs/RobustThinDielectric.ih b/modules/cpu/render/bsdfs/RobustThinDielectric.ih similarity index 100% rename from ospray/render/bsdfs/RobustThinDielectric.ih rename to modules/cpu/render/bsdfs/RobustThinDielectric.ih diff --git a/ospray/render/bsdfs/Scale.ih b/modules/cpu/render/bsdfs/Scale.ih similarity index 100% rename from ospray/render/bsdfs/Scale.ih rename to modules/cpu/render/bsdfs/Scale.ih diff --git a/ospray/render/bsdfs/ShadingContext.ih b/modules/cpu/render/bsdfs/ShadingContext.ih similarity index 100% rename from ospray/render/bsdfs/ShadingContext.ih rename to modules/cpu/render/bsdfs/ShadingContext.ih diff --git a/ospray/render/bsdfs/SheenDistribution.ih b/modules/cpu/render/bsdfs/SheenDistribution.ih similarity index 100% rename from ospray/render/bsdfs/SheenDistribution.ih rename to modules/cpu/render/bsdfs/SheenDistribution.ih diff --git a/ospray/render/bsdfs/Specular.ih b/modules/cpu/render/bsdfs/Specular.ih similarity index 100% rename from ospray/render/bsdfs/Specular.ih rename to modules/cpu/render/bsdfs/Specular.ih diff --git a/ospray/render/bsdfs/ThinDielectric.ih b/modules/cpu/render/bsdfs/ThinDielectric.ih similarity index 100% rename from ospray/render/bsdfs/ThinDielectric.ih rename to modules/cpu/render/bsdfs/ThinDielectric.ih diff --git a/ospray/render/bsdfs/ThinMicrofacetDielectric.ih b/modules/cpu/render/bsdfs/ThinMicrofacetDielectric.ih similarity index 100% rename from ospray/render/bsdfs/ThinMicrofacetDielectric.ih rename to modules/cpu/render/bsdfs/ThinMicrofacetDielectric.ih diff --git a/ospray/render/bsdfs/Transmission.ih b/modules/cpu/render/bsdfs/Transmission.ih similarity index 100% rename from ospray/render/bsdfs/Transmission.ih rename to modules/cpu/render/bsdfs/Transmission.ih diff --git a/ospray/render/bsdfs/Velvety.ih b/modules/cpu/render/bsdfs/Velvety.ih similarity index 100% rename from ospray/render/bsdfs/Velvety.ih rename to modules/cpu/render/bsdfs/Velvety.ih diff --git a/ospray/render/debug/DebugRenderer.cpp b/modules/cpu/render/debug/DebugRenderer.cpp similarity index 100% rename from ospray/render/debug/DebugRenderer.cpp rename to modules/cpu/render/debug/DebugRenderer.cpp diff --git a/ospray/render/debug/DebugRenderer.h b/modules/cpu/render/debug/DebugRenderer.h similarity index 100% rename from ospray/render/debug/DebugRenderer.h rename to modules/cpu/render/debug/DebugRenderer.h diff --git a/ospray/render/debug/DebugRenderer.ispc b/modules/cpu/render/debug/DebugRenderer.ispc similarity index 100% rename from ospray/render/debug/DebugRenderer.ispc rename to modules/cpu/render/debug/DebugRenderer.ispc diff --git a/ospray/render/debug/DebugRendererType.h b/modules/cpu/render/debug/DebugRendererType.h similarity index 100% rename from ospray/render/debug/DebugRendererType.h rename to modules/cpu/render/debug/DebugRendererType.h diff --git a/ospray/render/materials/Alloy.cpp b/modules/cpu/render/materials/Alloy.cpp similarity index 100% rename from ospray/render/materials/Alloy.cpp rename to modules/cpu/render/materials/Alloy.cpp diff --git a/ospray/render/materials/Alloy.h b/modules/cpu/render/materials/Alloy.h similarity index 100% rename from ospray/render/materials/Alloy.h rename to modules/cpu/render/materials/Alloy.h diff --git a/ospray/render/materials/Alloy.ispc b/modules/cpu/render/materials/Alloy.ispc similarity index 100% rename from ospray/render/materials/Alloy.ispc rename to modules/cpu/render/materials/Alloy.ispc diff --git a/ospray/render/materials/CarPaint.cpp b/modules/cpu/render/materials/CarPaint.cpp similarity index 100% rename from ospray/render/materials/CarPaint.cpp rename to modules/cpu/render/materials/CarPaint.cpp diff --git a/ospray/render/materials/CarPaint.h b/modules/cpu/render/materials/CarPaint.h similarity index 100% rename from ospray/render/materials/CarPaint.h rename to modules/cpu/render/materials/CarPaint.h diff --git a/ospray/render/materials/CarPaint.ispc b/modules/cpu/render/materials/CarPaint.ispc similarity index 100% rename from ospray/render/materials/CarPaint.ispc rename to modules/cpu/render/materials/CarPaint.ispc diff --git a/ospray/render/materials/Glass.cpp b/modules/cpu/render/materials/Glass.cpp similarity index 100% rename from ospray/render/materials/Glass.cpp rename to modules/cpu/render/materials/Glass.cpp diff --git a/ospray/render/materials/Glass.h b/modules/cpu/render/materials/Glass.h similarity index 100% rename from ospray/render/materials/Glass.h rename to modules/cpu/render/materials/Glass.h diff --git a/ospray/render/materials/Glass.ispc b/modules/cpu/render/materials/Glass.ispc similarity index 100% rename from ospray/render/materials/Glass.ispc rename to modules/cpu/render/materials/Glass.ispc diff --git a/ospray/render/materials/Luminous.cpp b/modules/cpu/render/materials/Luminous.cpp similarity index 100% rename from ospray/render/materials/Luminous.cpp rename to modules/cpu/render/materials/Luminous.cpp diff --git a/ospray/render/materials/Luminous.h b/modules/cpu/render/materials/Luminous.h similarity index 100% rename from ospray/render/materials/Luminous.h rename to modules/cpu/render/materials/Luminous.h diff --git a/ospray/render/materials/Luminous.ispc b/modules/cpu/render/materials/Luminous.ispc similarity index 100% rename from ospray/render/materials/Luminous.ispc rename to modules/cpu/render/materials/Luminous.ispc diff --git a/ospray/render/materials/Material.ih b/modules/cpu/render/materials/Material.ih similarity index 100% rename from ospray/render/materials/Material.ih rename to modules/cpu/render/materials/Material.ih diff --git a/ospray/render/materials/Material.ispc b/modules/cpu/render/materials/Material.ispc similarity index 100% rename from ospray/render/materials/Material.ispc rename to modules/cpu/render/materials/Material.ispc diff --git a/ospray/render/materials/Medium.ih b/modules/cpu/render/materials/Medium.ih similarity index 100% rename from ospray/render/materials/Medium.ih rename to modules/cpu/render/materials/Medium.ih diff --git a/ospray/render/materials/Metal.cpp b/modules/cpu/render/materials/Metal.cpp similarity index 100% rename from ospray/render/materials/Metal.cpp rename to modules/cpu/render/materials/Metal.cpp diff --git a/ospray/render/materials/Metal.h b/modules/cpu/render/materials/Metal.h similarity index 100% rename from ospray/render/materials/Metal.h rename to modules/cpu/render/materials/Metal.h diff --git a/ospray/render/materials/Metal.ispc b/modules/cpu/render/materials/Metal.ispc similarity index 100% rename from ospray/render/materials/Metal.ispc rename to modules/cpu/render/materials/Metal.ispc diff --git a/ospray/render/materials/MetallicPaint.cpp b/modules/cpu/render/materials/MetallicPaint.cpp similarity index 100% rename from ospray/render/materials/MetallicPaint.cpp rename to modules/cpu/render/materials/MetallicPaint.cpp diff --git a/ospray/render/materials/MetallicPaint.h b/modules/cpu/render/materials/MetallicPaint.h similarity index 100% rename from ospray/render/materials/MetallicPaint.h rename to modules/cpu/render/materials/MetallicPaint.h diff --git a/ospray/render/materials/MetallicPaint.ispc b/modules/cpu/render/materials/MetallicPaint.ispc similarity index 100% rename from ospray/render/materials/MetallicPaint.ispc rename to modules/cpu/render/materials/MetallicPaint.ispc diff --git a/ospray/render/materials/Mix.cpp b/modules/cpu/render/materials/Mix.cpp similarity index 100% rename from ospray/render/materials/Mix.cpp rename to modules/cpu/render/materials/Mix.cpp diff --git a/ospray/render/materials/Mix.h b/modules/cpu/render/materials/Mix.h similarity index 100% rename from ospray/render/materials/Mix.h rename to modules/cpu/render/materials/Mix.h diff --git a/ospray/render/materials/Mix.ispc b/modules/cpu/render/materials/Mix.ispc similarity index 100% rename from ospray/render/materials/Mix.ispc rename to modules/cpu/render/materials/Mix.ispc diff --git a/ospray/render/materials/OBJ.cpp b/modules/cpu/render/materials/OBJ.cpp similarity index 100% rename from ospray/render/materials/OBJ.cpp rename to modules/cpu/render/materials/OBJ.cpp diff --git a/ospray/render/materials/OBJ.h b/modules/cpu/render/materials/OBJ.h similarity index 100% rename from ospray/render/materials/OBJ.h rename to modules/cpu/render/materials/OBJ.h diff --git a/ospray/render/materials/OBJ.ih b/modules/cpu/render/materials/OBJ.ih similarity index 100% rename from ospray/render/materials/OBJ.ih rename to modules/cpu/render/materials/OBJ.ih diff --git a/ospray/render/materials/OBJ.ispc b/modules/cpu/render/materials/OBJ.ispc similarity index 100% rename from ospray/render/materials/OBJ.ispc rename to modules/cpu/render/materials/OBJ.ispc diff --git a/ospray/render/materials/Plastic.cpp b/modules/cpu/render/materials/Plastic.cpp similarity index 100% rename from ospray/render/materials/Plastic.cpp rename to modules/cpu/render/materials/Plastic.cpp diff --git a/ospray/render/materials/Plastic.h b/modules/cpu/render/materials/Plastic.h similarity index 100% rename from ospray/render/materials/Plastic.h rename to modules/cpu/render/materials/Plastic.h diff --git a/ospray/render/materials/Plastic.ispc b/modules/cpu/render/materials/Plastic.ispc similarity index 100% rename from ospray/render/materials/Plastic.ispc rename to modules/cpu/render/materials/Plastic.ispc diff --git a/ospray/render/materials/Principled.cpp b/modules/cpu/render/materials/Principled.cpp similarity index 100% rename from ospray/render/materials/Principled.cpp rename to modules/cpu/render/materials/Principled.cpp diff --git a/ospray/render/materials/Principled.h b/modules/cpu/render/materials/Principled.h similarity index 100% rename from ospray/render/materials/Principled.h rename to modules/cpu/render/materials/Principled.h diff --git a/ospray/render/materials/Principled.ispc b/modules/cpu/render/materials/Principled.ispc similarity index 100% rename from ospray/render/materials/Principled.ispc rename to modules/cpu/render/materials/Principled.ispc diff --git a/ospray/render/materials/ThinGlass.cpp b/modules/cpu/render/materials/ThinGlass.cpp similarity index 100% rename from ospray/render/materials/ThinGlass.cpp rename to modules/cpu/render/materials/ThinGlass.cpp diff --git a/ospray/render/materials/ThinGlass.h b/modules/cpu/render/materials/ThinGlass.h similarity index 100% rename from ospray/render/materials/ThinGlass.h rename to modules/cpu/render/materials/ThinGlass.h diff --git a/ospray/render/materials/ThinGlass.ispc b/modules/cpu/render/materials/ThinGlass.ispc similarity index 100% rename from ospray/render/materials/ThinGlass.ispc rename to modules/cpu/render/materials/ThinGlass.ispc diff --git a/ospray/render/materials/Velvet.cpp b/modules/cpu/render/materials/Velvet.cpp similarity index 100% rename from ospray/render/materials/Velvet.cpp rename to modules/cpu/render/materials/Velvet.cpp diff --git a/ospray/render/materials/Velvet.h b/modules/cpu/render/materials/Velvet.h similarity index 100% rename from ospray/render/materials/Velvet.h rename to modules/cpu/render/materials/Velvet.h diff --git a/ospray/render/materials/Velvet.ispc b/modules/cpu/render/materials/Velvet.ispc similarity index 100% rename from ospray/render/materials/Velvet.ispc rename to modules/cpu/render/materials/Velvet.ispc diff --git a/ospray/render/pathtracer/GeometryLight.ih b/modules/cpu/render/pathtracer/GeometryLight.ih similarity index 100% rename from ospray/render/pathtracer/GeometryLight.ih rename to modules/cpu/render/pathtracer/GeometryLight.ih diff --git a/ospray/render/pathtracer/GeometryLight.ispc b/modules/cpu/render/pathtracer/GeometryLight.ispc similarity index 100% rename from ospray/render/pathtracer/GeometryLight.ispc rename to modules/cpu/render/pathtracer/GeometryLight.ispc diff --git a/ospray/render/pathtracer/NextEventEstimation.ih b/modules/cpu/render/pathtracer/NextEventEstimation.ih similarity index 100% rename from ospray/render/pathtracer/NextEventEstimation.ih rename to modules/cpu/render/pathtracer/NextEventEstimation.ih diff --git a/ospray/render/pathtracer/NextEventEstimation.ispc b/modules/cpu/render/pathtracer/NextEventEstimation.ispc similarity index 100% rename from ospray/render/pathtracer/NextEventEstimation.ispc rename to modules/cpu/render/pathtracer/NextEventEstimation.ispc diff --git a/ospray/render/pathtracer/PathSampler.ih b/modules/cpu/render/pathtracer/PathSampler.ih similarity index 100% rename from ospray/render/pathtracer/PathSampler.ih rename to modules/cpu/render/pathtracer/PathSampler.ih diff --git a/ospray/render/pathtracer/PathSampler.ispc b/modules/cpu/render/pathtracer/PathSampler.ispc similarity index 100% rename from ospray/render/pathtracer/PathSampler.ispc rename to modules/cpu/render/pathtracer/PathSampler.ispc diff --git a/ospray/render/pathtracer/PathStructs.ih b/modules/cpu/render/pathtracer/PathStructs.ih similarity index 100% rename from ospray/render/pathtracer/PathStructs.ih rename to modules/cpu/render/pathtracer/PathStructs.ih diff --git a/ospray/render/pathtracer/PathTracer.cpp b/modules/cpu/render/pathtracer/PathTracer.cpp similarity index 100% rename from ospray/render/pathtracer/PathTracer.cpp rename to modules/cpu/render/pathtracer/PathTracer.cpp diff --git a/ospray/render/pathtracer/PathTracer.h b/modules/cpu/render/pathtracer/PathTracer.h similarity index 100% rename from ospray/render/pathtracer/PathTracer.h rename to modules/cpu/render/pathtracer/PathTracer.h diff --git a/ospray/render/pathtracer/PathTracer.ih b/modules/cpu/render/pathtracer/PathTracer.ih similarity index 100% rename from ospray/render/pathtracer/PathTracer.ih rename to modules/cpu/render/pathtracer/PathTracer.ih diff --git a/ospray/render/pathtracer/PathTracer.ispc b/modules/cpu/render/pathtracer/PathTracer.ispc similarity index 100% rename from ospray/render/pathtracer/PathTracer.ispc rename to modules/cpu/render/pathtracer/PathTracer.ispc diff --git a/ospray/render/pathtracer/PathTracerDefines.ih b/modules/cpu/render/pathtracer/PathTracerDefines.ih similarity index 100% rename from ospray/render/pathtracer/PathTracerDefines.ih rename to modules/cpu/render/pathtracer/PathTracerDefines.ih diff --git a/ospray/render/pathtracer/PathTracerUtil.ih b/modules/cpu/render/pathtracer/PathTracerUtil.ih similarity index 100% rename from ospray/render/pathtracer/PathTracerUtil.ih rename to modules/cpu/render/pathtracer/PathTracerUtil.ih diff --git a/ospray/render/pathtracer/Scattering.ih b/modules/cpu/render/pathtracer/Scattering.ih similarity index 100% rename from ospray/render/pathtracer/Scattering.ih rename to modules/cpu/render/pathtracer/Scattering.ih diff --git a/ospray/render/pathtracer/ShadowCatcher.ih b/modules/cpu/render/pathtracer/ShadowCatcher.ih similarity index 100% rename from ospray/render/pathtracer/ShadowCatcher.ih rename to modules/cpu/render/pathtracer/ShadowCatcher.ih diff --git a/ospray/render/pathtracer/ShadowCatcher.ispc b/modules/cpu/render/pathtracer/ShadowCatcher.ispc similarity index 100% rename from ospray/render/pathtracer/ShadowCatcher.ispc rename to modules/cpu/render/pathtracer/ShadowCatcher.ispc diff --git a/ospray/render/pathtracer/TransparentShadow.ih b/modules/cpu/render/pathtracer/TransparentShadow.ih similarity index 100% rename from ospray/render/pathtracer/TransparentShadow.ih rename to modules/cpu/render/pathtracer/TransparentShadow.ih diff --git a/ospray/render/pathtracer/TransparentShadow.ispc b/modules/cpu/render/pathtracer/TransparentShadow.ispc similarity index 100% rename from ospray/render/pathtracer/TransparentShadow.ispc rename to modules/cpu/render/pathtracer/TransparentShadow.ispc diff --git a/ospray/render/pathtracer/VirtualLight.ih b/modules/cpu/render/pathtracer/VirtualLight.ih similarity index 100% rename from ospray/render/pathtracer/VirtualLight.ih rename to modules/cpu/render/pathtracer/VirtualLight.ih diff --git a/ospray/render/pathtracer/VirtualLight.ispc b/modules/cpu/render/pathtracer/VirtualLight.ispc similarity index 100% rename from ospray/render/pathtracer/VirtualLight.ispc rename to modules/cpu/render/pathtracer/VirtualLight.ispc diff --git a/ospray/render/pathtracer/volumes/HenyeyGreenstein.ih b/modules/cpu/render/pathtracer/volumes/HenyeyGreenstein.ih similarity index 100% rename from ospray/render/pathtracer/volumes/HenyeyGreenstein.ih rename to modules/cpu/render/pathtracer/volumes/HenyeyGreenstein.ih diff --git a/ospray/render/pathtracer/volumes/VolumeSampler.ih b/modules/cpu/render/pathtracer/volumes/VolumeSampler.ih similarity index 100% rename from ospray/render/pathtracer/volumes/VolumeSampler.ih rename to modules/cpu/render/pathtracer/volumes/VolumeSampler.ih diff --git a/ospray/render/pathtracer/volumes/VolumeSampler.ispc b/modules/cpu/render/pathtracer/volumes/VolumeSampler.ispc similarity index 100% rename from ospray/render/pathtracer/volumes/VolumeSampler.ispc rename to modules/cpu/render/pathtracer/volumes/VolumeSampler.ispc diff --git a/ospray/render/registration.cpp b/modules/cpu/render/registration.cpp similarity index 100% rename from ospray/render/registration.cpp rename to modules/cpu/render/registration.cpp diff --git a/ospray/render/registration.h b/modules/cpu/render/registration.h similarity index 100% rename from ospray/render/registration.h rename to modules/cpu/render/registration.h diff --git a/ospray/render/scivis/SciVis.cpp b/modules/cpu/render/scivis/SciVis.cpp similarity index 100% rename from ospray/render/scivis/SciVis.cpp rename to modules/cpu/render/scivis/SciVis.cpp diff --git a/ospray/render/scivis/SciVis.h b/modules/cpu/render/scivis/SciVis.h similarity index 100% rename from ospray/render/scivis/SciVis.h rename to modules/cpu/render/scivis/SciVis.h diff --git a/ospray/render/scivis/SciVis.ih b/modules/cpu/render/scivis/SciVis.ih similarity index 100% rename from ospray/render/scivis/SciVis.ih rename to modules/cpu/render/scivis/SciVis.ih diff --git a/ospray/render/scivis/SciVis.ispc b/modules/cpu/render/scivis/SciVis.ispc similarity index 100% rename from ospray/render/scivis/SciVis.ispc rename to modules/cpu/render/scivis/SciVis.ispc diff --git a/ospray/render/scivis/lightAlpha.ispc b/modules/cpu/render/scivis/lightAlpha.ispc similarity index 100% rename from ospray/render/scivis/lightAlpha.ispc rename to modules/cpu/render/scivis/lightAlpha.ispc diff --git a/ospray/render/scivis/surfaces.ih b/modules/cpu/render/scivis/surfaces.ih similarity index 100% rename from ospray/render/scivis/surfaces.ih rename to modules/cpu/render/scivis/surfaces.ih diff --git a/ospray/render/scivis/surfaces.ispc b/modules/cpu/render/scivis/surfaces.ispc similarity index 100% rename from ospray/render/scivis/surfaces.ispc rename to modules/cpu/render/scivis/surfaces.ispc diff --git a/ospray/render/scivis/volumes.ih b/modules/cpu/render/scivis/volumes.ih similarity index 100% rename from ospray/render/scivis/volumes.ih rename to modules/cpu/render/scivis/volumes.ih diff --git a/ospray/render/scivis/volumes.ispc b/modules/cpu/render/scivis/volumes.ispc similarity index 100% rename from ospray/render/scivis/volumes.ispc rename to modules/cpu/render/scivis/volumes.ispc diff --git a/ospray/render/shaders/Flakes.ih b/modules/cpu/render/shaders/Flakes.ih similarity index 100% rename from ospray/render/shaders/Flakes.ih rename to modules/cpu/render/shaders/Flakes.ih diff --git a/ospray/render/shaders/Noise.ih b/modules/cpu/render/shaders/Noise.ih similarity index 100% rename from ospray/render/shaders/Noise.ih rename to modules/cpu/render/shaders/Noise.ih diff --git a/ospray/render/util.h b/modules/cpu/render/util.h similarity index 100% rename from ospray/render/util.h rename to modules/cpu/render/util.h diff --git a/ospray/render/util.ih b/modules/cpu/render/util.ih similarity index 100% rename from ospray/render/util.ih rename to modules/cpu/render/util.ih diff --git a/ospray/render/util.ispc b/modules/cpu/render/util.ispc similarity index 100% rename from ospray/render/util.ispc rename to modules/cpu/render/util.ispc diff --git a/ospray/texture/Texture.cpp b/modules/cpu/texture/Texture.cpp similarity index 100% rename from ospray/texture/Texture.cpp rename to modules/cpu/texture/Texture.cpp diff --git a/ospray/texture/Texture.h b/modules/cpu/texture/Texture.h similarity index 100% rename from ospray/texture/Texture.h rename to modules/cpu/texture/Texture.h diff --git a/ospray/texture/Texture2D.cpp b/modules/cpu/texture/Texture2D.cpp similarity index 100% rename from ospray/texture/Texture2D.cpp rename to modules/cpu/texture/Texture2D.cpp diff --git a/ospray/texture/Texture2D.h b/modules/cpu/texture/Texture2D.h similarity index 100% rename from ospray/texture/Texture2D.h rename to modules/cpu/texture/Texture2D.h diff --git a/ospray/texture/Texture2D.ih b/modules/cpu/texture/Texture2D.ih similarity index 100% rename from ospray/texture/Texture2D.ih rename to modules/cpu/texture/Texture2D.ih diff --git a/ospray/texture/Texture2D.ispc b/modules/cpu/texture/Texture2D.ispc similarity index 100% rename from ospray/texture/Texture2D.ispc rename to modules/cpu/texture/Texture2D.ispc diff --git a/ospray/texture/TextureParam.ih b/modules/cpu/texture/TextureParam.ih similarity index 100% rename from ospray/texture/TextureParam.ih rename to modules/cpu/texture/TextureParam.ih diff --git a/ospray/texture/TextureParam.ispc b/modules/cpu/texture/TextureParam.ispc similarity index 100% rename from ospray/texture/TextureParam.ispc rename to modules/cpu/texture/TextureParam.ispc diff --git a/ospray/texture/TextureVolume.cpp b/modules/cpu/texture/TextureVolume.cpp similarity index 100% rename from ospray/texture/TextureVolume.cpp rename to modules/cpu/texture/TextureVolume.cpp diff --git a/ospray/texture/TextureVolume.h b/modules/cpu/texture/TextureVolume.h similarity index 100% rename from ospray/texture/TextureVolume.h rename to modules/cpu/texture/TextureVolume.h diff --git a/ospray/texture/TextureVolume.ispc b/modules/cpu/texture/TextureVolume.ispc similarity index 100% rename from ospray/texture/TextureVolume.ispc rename to modules/cpu/texture/TextureVolume.ispc diff --git a/ospray/texture/registration.cpp b/modules/cpu/texture/registration.cpp similarity index 100% rename from ospray/texture/registration.cpp rename to modules/cpu/texture/registration.cpp diff --git a/ospray/texture/registration.h b/modules/cpu/texture/registration.h similarity index 100% rename from ospray/texture/registration.h rename to modules/cpu/texture/registration.h diff --git a/ospray/volume/Volume.cpp b/modules/cpu/volume/Volume.cpp similarity index 100% rename from ospray/volume/Volume.cpp rename to modules/cpu/volume/Volume.cpp diff --git a/ospray/volume/Volume.h b/modules/cpu/volume/Volume.h similarity index 97% rename from ospray/volume/Volume.h rename to modules/cpu/volume/Volume.h index 8a348d3362..5ebe92bd20 100644 --- a/ospray/volume/Volume.h +++ b/modules/cpu/volume/Volume.h @@ -3,7 +3,7 @@ #pragma once -#include "api/ISPCDevice.h" +#include "ISPCDevice.h" #include "common/Managed.h" // embree #include "embree3/rtcore.h" diff --git a/ospray/volume/Volume.ih b/modules/cpu/volume/Volume.ih similarity index 100% rename from ospray/volume/Volume.ih rename to modules/cpu/volume/Volume.ih diff --git a/ospray/volume/Volume.ispc b/modules/cpu/volume/Volume.ispc similarity index 100% rename from ospray/volume/Volume.ispc rename to modules/cpu/volume/Volume.ispc diff --git a/ospray/volume/VolumetricModel.cpp b/modules/cpu/volume/VolumetricModel.cpp similarity index 100% rename from ospray/volume/VolumetricModel.cpp rename to modules/cpu/volume/VolumetricModel.cpp diff --git a/ospray/volume/VolumetricModel.h b/modules/cpu/volume/VolumetricModel.h similarity index 100% rename from ospray/volume/VolumetricModel.h rename to modules/cpu/volume/VolumetricModel.h diff --git a/ospray/volume/VolumetricModel.ih b/modules/cpu/volume/VolumetricModel.ih similarity index 100% rename from ospray/volume/VolumetricModel.ih rename to modules/cpu/volume/VolumetricModel.ih diff --git a/ospray/volume/VolumetricModel.ispc b/modules/cpu/volume/VolumetricModel.ispc similarity index 100% rename from ospray/volume/VolumetricModel.ispc rename to modules/cpu/volume/VolumetricModel.ispc diff --git a/ospray/volume/transferFunction/LinearTransferFunction.cpp b/modules/cpu/volume/transferFunction/LinearTransferFunction.cpp similarity index 100% rename from ospray/volume/transferFunction/LinearTransferFunction.cpp rename to modules/cpu/volume/transferFunction/LinearTransferFunction.cpp diff --git a/ospray/volume/transferFunction/LinearTransferFunction.h b/modules/cpu/volume/transferFunction/LinearTransferFunction.h similarity index 100% rename from ospray/volume/transferFunction/LinearTransferFunction.h rename to modules/cpu/volume/transferFunction/LinearTransferFunction.h diff --git a/ospray/volume/transferFunction/LinearTransferFunction.ih b/modules/cpu/volume/transferFunction/LinearTransferFunction.ih similarity index 100% rename from ospray/volume/transferFunction/LinearTransferFunction.ih rename to modules/cpu/volume/transferFunction/LinearTransferFunction.ih diff --git a/ospray/volume/transferFunction/LinearTransferFunction.ispc b/modules/cpu/volume/transferFunction/LinearTransferFunction.ispc similarity index 100% rename from ospray/volume/transferFunction/LinearTransferFunction.ispc rename to modules/cpu/volume/transferFunction/LinearTransferFunction.ispc diff --git a/ospray/volume/transferFunction/TransferFunction.cpp b/modules/cpu/volume/transferFunction/TransferFunction.cpp similarity index 100% rename from ospray/volume/transferFunction/TransferFunction.cpp rename to modules/cpu/volume/transferFunction/TransferFunction.cpp diff --git a/ospray/volume/transferFunction/TransferFunction.h b/modules/cpu/volume/transferFunction/TransferFunction.h similarity index 100% rename from ospray/volume/transferFunction/TransferFunction.h rename to modules/cpu/volume/transferFunction/TransferFunction.h diff --git a/ospray/volume/transferFunction/TransferFunction.ih b/modules/cpu/volume/transferFunction/TransferFunction.ih similarity index 100% rename from ospray/volume/transferFunction/TransferFunction.ih rename to modules/cpu/volume/transferFunction/TransferFunction.ih diff --git a/ospray/volume/transferFunction/TransferFunction.ispc b/modules/cpu/volume/transferFunction/TransferFunction.ispc similarity index 100% rename from ospray/volume/transferFunction/TransferFunction.ispc rename to modules/cpu/volume/transferFunction/TransferFunction.ispc diff --git a/ospray/volume/transferFunction/registration.cpp b/modules/cpu/volume/transferFunction/registration.cpp similarity index 100% rename from ospray/volume/transferFunction/registration.cpp rename to modules/cpu/volume/transferFunction/registration.cpp diff --git a/ospray/volume/transferFunction/registration.h b/modules/cpu/volume/transferFunction/registration.h similarity index 100% rename from ospray/volume/transferFunction/registration.h rename to modules/cpu/volume/transferFunction/registration.h diff --git a/modules/denoiser/CMakeLists.txt b/modules/denoiser/CMakeLists.txt index 13686f74da..22a73ef42d 100644 --- a/modules/denoiser/CMakeLists.txt +++ b/modules/denoiser/CMakeLists.txt @@ -1,11 +1,11 @@ -## Copyright 2009-2020 Intel Corporation +## Copyright 2009-2021 Intel Corporation ## SPDX-License-Identifier: Apache-2.0 cmake_dependent_option( OSPRAY_MODULE_DENOISER "Build the Denoiser FrameOp module for denoising (requires OIDN)" OFF - OSPRAY_ENABLE_MODULES + OSPRAY_MODULE_CPU OFF ) @@ -22,7 +22,7 @@ generate_export_header(ospray_module_denoiser) target_link_libraries(ospray_module_denoiser PRIVATE OpenImageDenoise - ospray_module_ispc + ospray_module_cpu ) target_include_directories(ospray_module_denoiser diff --git a/modules/denoiser/DenoiseFrameOp.h b/modules/denoiser/DenoiseFrameOp.h index 428e0106fa..4092e283af 100644 --- a/modules/denoiser/DenoiseFrameOp.h +++ b/modules/denoiser/DenoiseFrameOp.h @@ -1,4 +1,4 @@ -// Copyright 2009-2020 Intel Corporation +// Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once @@ -6,7 +6,7 @@ // oidn #include "OpenImageDenoise/oidn.h" // ospray -#include "ospray/fb/ImageOp.h" +#include "fb/ImageOp.h" #include "ospray_module_denoiser_export.h" namespace ospray { diff --git a/modules/mpi/CMakeLists.txt b/modules/mpi/CMakeLists.txt index 53c732d2be..e8991fbb5c 100644 --- a/modules/mpi/CMakeLists.txt +++ b/modules/mpi/CMakeLists.txt @@ -5,15 +5,7 @@ cmake_dependent_option( OSPRAY_MODULE_MPI "Build the MPI module (Requires ISPC module)" OFF - OSPRAY_ENABLE_MODULES - OFF -) - -cmake_dependent_option( - OSPRAY_MPI_BUILD_TUTORIALS - "Enable MPI module sample apps" - ON - "OSPRAY_MODULE_MPI;OSPRAY_ENABLE_APPS;OSPRAY_APPS_EXAMPLES" + OSPRAY_MODULE_CPU OFF ) @@ -45,10 +37,10 @@ add_subdirectory(common) add_subdirectory(external) add_subdirectory(ospray) -if (OSPRAY_MPI_BUILD_TUTORIALS) +if (OSPRAY_ENABLE_APPS_EXAMPLES) add_subdirectory(tutorials) endif() -if (OSPRAY_APPS_TESTING) +if (OSPRAY_ENABLE_APPS_TESTING) add_subdirectory(testing) endif() diff --git a/modules/mpi/common/CMakeLists.txt b/modules/mpi/common/CMakeLists.txt index 12c8cf02d9..1302f72476 100644 --- a/modules/mpi/common/CMakeLists.txt +++ b/modules/mpi/common/CMakeLists.txt @@ -16,8 +16,7 @@ target_link_libraries(ospray_mpi_common Snappy::snappy PUBLIC rkcommon::rkcommon - ospray_sdk - ospray_module_ispc + ospray_module_cpu MPI::MPI_CXX ) diff --git a/modules/mpi/ospray/CMakeLists.txt b/modules/mpi/ospray/CMakeLists.txt index 91c86600e5..4932d6da30 100644 --- a/modules/mpi/ospray/CMakeLists.txt +++ b/modules/mpi/ospray/CMakeLists.txt @@ -54,8 +54,7 @@ target_link_libraries(ospray_module_mpi rkcommon::rkcommon ospray_mpi_common ospray_pico_bench - ospray_sdk - ospray_module_ispc + ospray_module_cpu ) target_include_directories(ospray_module_mpi diff --git a/modules/mpi/ospray/MPIDistributedDevice.cpp b/modules/mpi/ospray/MPIDistributedDevice.cpp index b9700a6d5a..6b75dc5281 100644 --- a/modules/mpi/ospray/MPIDistributedDevice.cpp +++ b/modules/mpi/ospray/MPIDistributedDevice.cpp @@ -4,7 +4,7 @@ #include "MPIDistributedDevice.h" #include #include "MPIDistributedDevice_ispc.h" -#include "api/ISPCDevice.h" +#include "ISPCDevice.h" #include "camera/Camera.h" #include "common/Data.h" #include "common/DistributedWorld.h" diff --git a/modules/mpi/ospray/MPIDistributedDevice.h b/modules/mpi/ospray/MPIDistributedDevice.h index e79302e8e2..357b1efd45 100644 --- a/modules/mpi/ospray/MPIDistributedDevice.h +++ b/modules/mpi/ospray/MPIDistributedDevice.h @@ -8,7 +8,7 @@ #include "common/Managed.h" #include "common/ObjectHandle.h" #include "openvkl/openvkl.h" -#include "ospray/render/LoadBalancer.h" +#include "render/LoadBalancer.h" namespace ospray { namespace mpi { diff --git a/modules/mpi/ospray/MPIOffloadWorker.cpp b/modules/mpi/ospray/MPIOffloadWorker.cpp index 15725eecae..7daf2c4a72 100644 --- a/modules/mpi/ospray/MPIOffloadWorker.cpp +++ b/modules/mpi/ospray/MPIOffloadWorker.cpp @@ -13,7 +13,7 @@ #include #include #include "MPIOffloadDevice.h" -#include "api/ISPCDevice.h" +#include "ISPCDevice.h" #include "common/Library.h" #include "common/MPIBcastFabric.h" #include "common/MPICommon.h" diff --git a/modules/mpi/ospray/ModuleInit.cpp b/modules/mpi/ospray/ModuleInit.cpp index d0893d6ddc..6f70bba447 100644 --- a/modules/mpi/ospray/ModuleInit.cpp +++ b/modules/mpi/ospray/ModuleInit.cpp @@ -16,7 +16,7 @@ extern "C" OSPError OSPRAY_DLLEXPORT ospray_module_init_mpi( if (status == OSP_NO_ERROR) { // Run the ISPC module's initialization function as well to register local // types - status = ospLoadModule("ispc"); + status = ospLoadModule("cpu"); } if (status == OSP_NO_ERROR) { diff --git a/modules/mpi/ospray/common/DistributedWorld.cpp b/modules/mpi/ospray/common/DistributedWorld.cpp index c6568d5156..57b4b69c0b 100644 --- a/modules/mpi/ospray/common/DistributedWorld.cpp +++ b/modules/mpi/ospray/common/DistributedWorld.cpp @@ -7,7 +7,7 @@ #include "DistributedWorld.h" #include "MPICommon.h" #include "Messaging.h" -#include "api/ISPCDevice.h" +#include "ISPCDevice.h" #include "common/Data.h" #include "common/DistributedWorld_ispc.h" diff --git a/modules/mpi/ospray/common/DistributedWorld.ih b/modules/mpi/ospray/common/DistributedWorld.ih index 991e93475d..34b34fb405 100644 --- a/modules/mpi/ospray/common/DistributedWorld.ih +++ b/modules/mpi/ospray/common/DistributedWorld.ih @@ -3,7 +3,7 @@ #pragma once -#include "ospray/common/World.ih" +#include "common/World.ih" struct DistributedWorld { diff --git a/modules/mpi/testing/CMakeLists.txt b/modules/mpi/testing/CMakeLists.txt index 7fce91d982..77e94c575d 100644 --- a/modules/mpi/testing/CMakeLists.txt +++ b/modules/mpi/testing/CMakeLists.txt @@ -13,7 +13,7 @@ target_link_libraries(ospMPIDistribTestSuite PRIVATE arcball_camera ospray_testing - ospray_module_ispc + ospray_module_cpu ospray_gtest_utils MPI::MPI_CXX ) diff --git a/modules/multiDevice/CMakeLists.txt b/modules/multiDevice/CMakeLists.txt index 072d03f51a..22643132c0 100644 --- a/modules/multiDevice/CMakeLists.txt +++ b/modules/multiDevice/CMakeLists.txt @@ -5,7 +5,7 @@ cmake_dependent_option( OSPRAY_MODULE_MULTIDEVICE "Build the multidevice module" OFF - OSPRAY_ENABLE_MODULES + OSPRAY_MODULE_CPU OFF ) @@ -37,8 +37,7 @@ ispc_target_add_sources(ospray_module_multidevice target_link_libraries(ospray_module_multidevice PRIVATE rkcommon::rkcommon - ospray_sdk - ospray_module_ispc + ospray_module_cpu ) target_include_directories(ospray_module_multidevice diff --git a/modules/multiDevice/ModuleInit.cpp b/modules/multiDevice/ModuleInit.cpp index 1ad3b7b9af..6a442406f1 100644 --- a/modules/multiDevice/ModuleInit.cpp +++ b/modules/multiDevice/ModuleInit.cpp @@ -15,7 +15,7 @@ extern "C" OSPError OSPRAY_DLLEXPORT ospray_module_init_multidevice( if (status == OSP_NO_ERROR) { // Run the ISPC module's initialization function as well to register local // types - status = ospLoadModule("ispc"); + status = ospLoadModule("cpu"); } if (status == OSP_NO_ERROR) { diff --git a/modules/multiDevice/MultiDevice.h b/modules/multiDevice/MultiDevice.h index b4fdb0e62a..e41c5918a8 100644 --- a/modules/multiDevice/MultiDevice.h +++ b/modules/multiDevice/MultiDevice.h @@ -5,7 +5,7 @@ #include #include -#include "api/ISPCDevice.h" +#include "ISPCDevice.h" #include "MultiDeviceLoadBalancer.h" #include "MultiDeviceObject.h" #include "MultiDeviceRenderTask.h" diff --git a/modules/pluggableGeometryExample/CMakeLists.txt b/modules/pluggableGeometryExample/CMakeLists.txt index 4bbf24d221..d13406c4cd 100644 --- a/modules/pluggableGeometryExample/CMakeLists.txt +++ b/modules/pluggableGeometryExample/CMakeLists.txt @@ -1,4 +1,4 @@ -## Copyright 2009-2020 Intel Corporation +## Copyright 2009-2021 Intel Corporation ## SPDX-License-Identifier: Apache-2.0 # Define an option to enable/disable this module. @@ -10,7 +10,7 @@ cmake_dependent_option( OSPRAY_MODULE_BILINEAR_PATCH "Build pluggable geometry example module (a Bilinear Patch)" OFF - OSPRAY_ENABLE_MODULES + OSPRAY_MODULE_CPU OFF ) diff --git a/modules/pluggableGeometryExample/ospray/CMakeLists.txt b/modules/pluggableGeometryExample/ospray/CMakeLists.txt index 2f7e6c97ac..7cb962a8d4 100644 --- a/modules/pluggableGeometryExample/ospray/CMakeLists.txt +++ b/modules/pluggableGeometryExample/ospray/CMakeLists.txt @@ -1,4 +1,4 @@ -## Copyright 2009-2020 Intel Corporation +## Copyright 2009-2021 Intel Corporation ## SPDX-License-Identifier: Apache-2.0 # this subdirectory builds the actual ospray plugin into a library @@ -45,4 +45,4 @@ ispc_target_add_sources(ospray_module_bilinear_patches moduleInit.cpp ) -target_link_libraries(ospray_module_bilinear_patches PUBLIC ospray_module_ispc) +target_link_libraries(ospray_module_bilinear_patches PUBLIC ospray_module_cpu) diff --git a/modules/pluggableGeometryExample/ospray/geometry/BilinearPatches.cpp b/modules/pluggableGeometryExample/ospray/geometry/BilinearPatches.cpp index 9846cd239e..d63afccbd2 100644 --- a/modules/pluggableGeometryExample/ospray/geometry/BilinearPatches.cpp +++ b/modules/pluggableGeometryExample/ospray/geometry/BilinearPatches.cpp @@ -5,7 +5,7 @@ // 'export'ed functions from the ispc file: #include "geometry/BilinearPatches_ispc.h" // ospray core: -#include +#include /*! _everything_ in the ospray core universe should _always_ be in the 'ospray' namespace. */ diff --git a/modules/pluggableGeometryExample/ospray/geometry/BilinearPatches.h b/modules/pluggableGeometryExample/ospray/geometry/BilinearPatches.h index 61e7b4b4e9..41ee0ceed4 100644 --- a/modules/pluggableGeometryExample/ospray/geometry/BilinearPatches.h +++ b/modules/pluggableGeometryExample/ospray/geometry/BilinearPatches.h @@ -1,4 +1,4 @@ -// Copyright 2009-2020 Intel Corporation +// Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once @@ -12,7 +12,7 @@ #include "rkcommon/math/box.h" #include "rkcommon/math/vec.h" // ospray: everything that's related to the ospray ray tracing core -#include "ospray/geometry/Geometry.h" +#include "geometry/Geometry.h" /*! _everything_ in the ospray core universe should _always_ be in the 'ospray' namespace. */ diff --git a/ospray/CMakeLists.txt b/ospray/CMakeLists.txt index 6b7ea27f3f..90f43fbf62 100644 --- a/ospray/CMakeLists.txt +++ b/ospray/CMakeLists.txt @@ -38,195 +38,6 @@ set(OSPRAY_SOURCES include/ospray/ospray_cpp/World.h ) - -set(OSPRAY_ISPC_DEVICE_SOURCES - ${OSPRAY_RESOURCE} - - ispc_tasksys.cpp - - api/ISPCDevice.h - api/ISPCDevice.cpp - api/ISPCDevice.ispc - - common/Data.ih - common/Data.cpp - common/Future.cpp - common/Group.cpp - common/Group.ispc - common/Instance.cpp - common/Instance.ispc - common/MotionTransform.cpp - common/Managed.cpp - common/OSPCommon.ispc - common/World.ih - common/World.ispc - common/World.cpp - - fb/FrameBuffer.ispc - fb/FrameBuffer.cpp - fb/FrameBufferView.cpp - fb/LocalFB.ispc - fb/LocalFB.cpp - fb/ImageOp.cpp - fb/Tile.h - fb/TileError.cpp - fb/registration.cpp - - fb/frame_ops/Blur.cpp - fb/frame_ops/Debug.cpp - fb/frame_ops/Depth.cpp - fb/frame_ops/SSAO.cpp - fb/frame_ops/SSAO.ispc - - fb/tile_ops/SaveTiles.cpp - fb/tile_ops/ToneMapper.cpp - fb/tile_ops/ToneMapper.ispc - - pf/PixelFilter.ih - pf/PixelFilter.ispc - pf/LUTPixelFilter.ih - pf/LUTPixelFilter.ispc - pf/PixelFilter.cpp - - camera/Camera.cpp - camera/Camera.ispc - camera/PerspectiveCamera.ispc - camera/PerspectiveCamera.cpp - camera/OrthographicCamera.ispc - camera/OrthographicCamera.cpp - camera/PanoramicCamera.ispc - camera/PanoramicCamera.cpp - camera/registration.cpp - - geometry/Geometry.ispc - geometry/Geometry.cpp - geometry/GeometricModel.ispc - geometry/GeometricModel.cpp - geometry/Boxes.ispc - geometry/Boxes.cpp - geometry/Subdivision.ispc - geometry/Subdivision.cpp - geometry/Curves.cpp - geometry/Curves.ispc - geometry/Spheres.cpp - geometry/Spheres.ispc - geometry/Isosurfaces.ispc - geometry/Isosurfaces.cpp - geometry/Planes.ispc - geometry/Planes.cpp - geometry/Mesh.cpp - geometry/Mesh.ispc - geometry/registration.cpp - - lights/Light.ispc - lights/Light.cpp - lights/AmbientLight.ispc - lights/AmbientLight.cpp - lights/DirectionalLight.cpp - lights/DirectionalLight.ispc - lights/PointLight.cpp - lights/PointLight.ispc - lights/SpotLight.cpp - lights/SpotLight.ispc - lights/QuadLight.cpp - lights/QuadLight.ispc - lights/CylinderLight.cpp - lights/CylinderLight.ispc - lights/HDRILight.cpp - lights/HDRILight.ispc - lights/SunSkyLight.cpp - lights/sky_model/sky_model.cpp - lights/registration.cpp - - math/Distribution1D.ispc - math/Distribution2D.ispc - math/sobol.ih - math/sobol.ispc - math/spectrum.h - math/spectrum.ih - math/spectrum.ispc - math/random.ih - math/halton.ih - math/halton.ispc - - render/LoadBalancer.cpp - render/Material.cpp - render/Renderer.ispc - render/Renderer.cpp - render/util.ispc - render/registration.cpp - - render/debug/DebugRenderer.cpp - render/debug/DebugRenderer.ispc - - render/ao/AORenderer.cpp - render/ao/AORenderer.ispc - render/ao/surfaces.ispc - render/ao/volumes.ispc - - render/scivis/SciVis.cpp - render/scivis/SciVis.ispc - render/scivis/surfaces.ispc - render/scivis/volumes.ispc - render/scivis/lightAlpha.ispc - - render/pathtracer/PathTracer.ispc - render/pathtracer/PathTracer.cpp - render/pathtracer/PathSampler.ispc - render/pathtracer/GeometryLight.ispc - render/pathtracer/VirtualLight.ispc - render/pathtracer/TransparentShadow.ispc - render/pathtracer/ShadowCatcher.ispc - render/pathtracer/NextEventEstimation.ispc - render/pathtracer/volumes/VolumeSampler.ispc - - render/bsdfs/MicrofacetAlbedoTables.ispc - render/materials/Material.ispc - render/materials/OBJ.ispc - render/materials/OBJ.cpp - render/materials/Principled.ispc - render/materials/Principled.cpp - render/materials/CarPaint.ispc - render/materials/CarPaint.cpp - render/materials/Velvet.ispc - render/materials/Velvet.cpp - render/materials/Metal.ispc - render/materials/Metal.cpp - render/materials/Alloy.ispc - render/materials/Alloy.cpp - render/materials/ThinGlass.ispc - render/materials/ThinGlass.cpp - render/materials/Glass.ispc - render/materials/Glass.cpp - render/materials/MetallicPaint.ispc - render/materials/MetallicPaint.cpp - render/materials/Plastic.ispc - render/materials/Plastic.cpp - render/materials/Luminous.ispc - render/materials/Luminous.cpp - render/materials/Mix.ispc - render/materials/Mix.cpp - - texture/Texture.cpp - texture/Texture2D.cpp - texture/Texture2D.ispc - texture/TextureVolume.cpp - texture/TextureVolume.ispc - texture/TextureParam.ispc - texture/registration.cpp - - volume/Volume.ispc - volume/Volume.cpp - volume/VolumetricModel.ispc - volume/VolumetricModel.cpp - - volume/transferFunction/LinearTransferFunction.ispc - volume/transferFunction/LinearTransferFunction.cpp - volume/transferFunction/TransferFunction.ispc - volume/transferFunction/TransferFunction.cpp - volume/transferFunction/registration.cpp -) - # ------------------------------------------------------- # Install SDK headers # ------------------------------------------------------- @@ -267,129 +78,6 @@ target_include_directories(ospray $ ) -############################################################## -# Build ISPC device -############################################################## - -option(OSPRAY_BUILD_ISPC_DEVICE "Build ISPC Device" ON) -mark_as_advanced(OSPRAY_BUILD_ISPC_DEVICE) - -if(OSPRAY_BUILD_ISPC_DEVICE) - - ## Windows-specifc ISPC symbol exports ## - - if (WIN32) - file(READ def_header.txt OSPRAY_DEF) - file(READ ispc_symbols.txt OSPRAY_ISPC_SYMBOLS_IN) - - foreach(isa ${OSPRAY_ISPC_TARGET_LIST}) - string(REPLACE "-i32x16" "" isa ${isa}) # strip avx512(knl|skx)-i32x16 - # add isa suffix - string(REPLACE "," ${isa} OSPRAY_ISPC_SYMBOLS ${OSPRAY_ISPC_SYMBOLS_IN}) - string(APPEND OSPRAY_DEF ${OSPRAY_ISPC_SYMBOLS}) - endforeach() - - file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/ospray_module_ispc.def.in "${OSPRAY_DEF}") - - # changes .def only if content changed, avoids unnecessary re-linking - configure_file(${CMAKE_CURRENT_BINARY_DIR}/ospray_module_ispc.def.in ospray_module_ispc.def COPYONLY) - - list(APPEND OSPRAY_ISPC_DEVICE_SOURCES ospray_module_ispc.def) - endif() - - ## ISPC Device-specific options ## - - set(OSPRAY_TILE_SIZE 64 CACHE STRING "Tile size (x,y dimensions)") - set_property(CACHE OSPRAY_TILE_SIZE PROPERTY STRINGS 8 16 32 64 128 256 512) - mark_as_advanced(OSPRAY_TILE_SIZE) - - if (WIN32) - set(TILE_STACK_DEFAULT_SIZE 64) - elseif (APPLE) - set(TILE_STACK_DEFAULT_SIZE 32) - else () - set(TILE_STACK_DEFAULT_SIZE 128) - endif() - - set(OSPRAY_MAX_STACK_TILE_SIZE ${TILE_STACK_DEFAULT_SIZE} CACHE STRING - "Max size for tile to remain allocated on the stack") - set_property(CACHE OSPRAY_MAX_STACK_TILE_SIZE PROPERTY STRINGS 8 16 32 64 128 256 512) - mark_as_advanced(OSPRAY_MAX_STACK_TILE_SIZE) - - set(OSPRAY_PIXELS_PER_JOB 64 CACHE STRING - "Must be multiple of largest vector width *and* <= OSPRAY_TILE_SIZE") - mark_as_advanced(OSPRAY_PIXELS_PER_JOB) - - option(OSPRAY_DEBUG_NO_TASKING - "Remove tasking parallelism system for debugging" - OFF) - mark_as_advanced(OSPRAY_DEBUG_NO_TASKING) - - configure_file(common/OSPConfig.h.in ../OSPConfig.h) - install(FILES ${CMAKE_BINARY_DIR}/OSPConfig.h - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/ospray/SDK - COMPONENT devel - ) - - ## Build the module ## - - ispc_include_directories( - ${CMAKE_SOURCE_DIR}/ospray/include - ${CMAKE_SOURCE_DIR}/ospray - ${CMAKE_SOURCE_DIR} - ${PROJECT_BINARY_DIR} - ${RKCOMMON_INCLUDE_DIRS} - ${EMBREE_INCLUDE_DIRS} - ${OPENVKL_INCLUDE_DIRS} - ) - add_library(ospray_module_ispc SHARED) - - ispc_target_add_sources(ospray_module_ispc ${OSPRAY_ISPC_DEVICE_SOURCES}) - - ospray_install_library(ospray_module_ispc lib) - - target_link_libraries(ospray_module_ispc - PUBLIC - ospray - rkcommon::rkcommon - $ - $ - ) - - target_include_directories(ospray_module_ispc - PUBLIC - $ - $ - $ - # NOTE(jda) - the following includes are missing despite PUBLIC linking - $ - $ - ####################################################################### - $ - $ - ) - - target_compile_definitions(ospray_module_ispc - PUBLIC - TILE_SIZE=${OSPRAY_TILE_SIZE} - MAX_TILE_SIZE=${OSPRAY_MAX_STACK_TILE_SIZE} - RENDERTILE_PIXELS_PER_JOB=${OSPRAY_PIXELS_PER_JOB} - $<$:OSPRAY_SERIAL_RENDERING> - ) - -endif() - -############################################################## -# Additional interface targets -############################################################## - -add_library(ospray_sdk INTERFACE) -target_link_libraries(ospray_sdk -INTERFACE - ospray_module_ispc rkcommon::rkcommon -) -ospray_install_target(ospray_sdk devel) - ############################################################## # Configure find_package files ############################################################## diff --git a/ospray/api/API.cpp b/ospray/api/API.cpp index 8112d323cf..1ea91a4a47 100644 --- a/ospray/api/API.cpp +++ b/ospray/api/API.cpp @@ -136,7 +136,7 @@ extern "C" OSPError ospInit(int *_ac, const char **_av) OSPRAY_CATCH_BEGIN auto device_name = OSPRAY_DEVICE.value(); currentDevice = Device::createDevice(device_name.c_str()); } else { - ospLoadModule("ispc"); + ospLoadModule("cpu"); currentDevice = Device::createDevice("cpu"); } } @@ -334,12 +334,16 @@ extern "C" void ospDeviceRetain(OSPDevice _object) OSPRAY_CATCH_BEGIN } OSPRAY_CATCH_END() -extern "C" OSPError ospLoadModule(const char *moduleName) OSPRAY_CATCH_BEGIN +extern "C" OSPError ospLoadModule(const char *module_name) OSPRAY_CATCH_BEGIN { + std::string moduleName(module_name); + if (moduleName == "ispc") // XXX backwards compatibility + moduleName = "cpu"; + if (deviceIsSet()) { - return (OSPError)currentDevice().loadModule(moduleName); + return (OSPError)currentDevice().loadModule(moduleName.c_str()); } else { - return loadLocalModule(moduleName); + return loadLocalModule(moduleName.c_str()); } } OSPRAY_CATCH_END(OSP_UNKNOWN_ERROR) diff --git a/ospray/api/Device.cpp b/ospray/api/Device.cpp index 6783812a21..e03a25e269 100644 --- a/ospray/api/Device.cpp +++ b/ospray/api/Device.cpp @@ -1,4 +1,4 @@ -ļ»æ// Copyright 2009-2020 Intel Corporation +// Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // ospray @@ -45,8 +45,8 @@ Device *Device::createDevice(const char *type) if (!repo.libraryExists("ospray")) repo.addDefaultLibrary(); - if (!repo.libraryExists("ospray_module_ispc") && type == std::string("cpu")) - repo.add("ospray_module_ispc"); + if (!repo.libraryExists("ospray_module_cpu") && type == std::string("cpu")) + repo.add("ospray_module_cpu"); return createInstanceHelper(type, g_devicesMap[type]); } diff --git a/ospray/common/OSPCommon.h b/ospray/common/OSPCommon.h index 8dbb0145c9..b30627ca61 100644 --- a/ospray/common/OSPCommon.h +++ b/ospray/common/OSPCommon.h @@ -52,7 +52,7 @@ typedef int ssize_t; #define OSPRAY_CORE_INTERFACE OSPRAY_INTERFACE #ifdef _WIN32 -#ifdef ospray_module_ispc_EXPORTS +#ifdef ospray_module_cpu_EXPORTS #define OSPRAY_MODULE_ISPC_INTERFACE __declspec(dllexport) #else #define OSPRAY_MODULE_ISPC_INTERFACE __declspec(dllimport) diff --git a/ospray/include/ospray/ospray.h b/ospray/include/ospray/ospray.h index 448a60c771..140f0b9e76 100644 --- a/ospray/include/ospray/ospray.h +++ b/ospray/include/ospray/ospray.h @@ -160,7 +160,7 @@ OSPRAY_INTERFACE void ospShutdown(); // Create an OSPRay engine backend using explicit device string. OSPRAY_INTERFACE OSPDevice ospNewDevice( - const char *deviceType OSP_DEFAULT_VAL("default")); + const char *deviceType OSP_DEFAULT_VAL("cpu")); // Set current device the API responds to OSPRAY_INTERFACE void ospSetCurrentDevice(OSPDevice); diff --git a/scripts/release/linux.sh b/scripts/release/linux.sh index 3504ad8370..811931737c 100755 --- a/scripts/release/linux.sh +++ b/scripts/release/linux.sh @@ -119,12 +119,12 @@ check_symbols libospray.so GLIBC 2 17 0 check_symbols libospray.so GLIBCXX 3 4 19 check_symbols libospray.so CXXABI 1 3 7 -check_symbols libospray_module_ispc.so GLIBC 2 17 0 -check_symbols libospray_module_ispc.so GLIBCXX 3 4 19 -check_symbols libospray_module_ispc.so CXXABI 1 3 7 +check_symbols libospray_module_cpu.so GLIBC 2 17 0 +check_symbols libospray_module_cpu.so GLIBCXX 3 4 19 +check_symbols libospray_module_cpu.so CXXABI 1 3 7 check_lib_dependency_error libospray.so libimf.so -check_lib_dependency_error libospray_module_ispc.so libimf.so +check_lib_dependency_error libospray_module_cpu.so libimf.so check_symbols libospray_module_mpi.so GLIBC 2 17 0 check_symbols libospray_module_mpi.so GLIBCXX 3 4 19 diff --git a/scripts/superbuild/CMakeLists.txt b/scripts/superbuild/CMakeLists.txt index 43e95ab719..1c6d4ed1ff 100644 --- a/scripts/superbuild/CMakeLists.txt +++ b/scripts/superbuild/CMakeLists.txt @@ -159,6 +159,7 @@ option(BUILD_DEPENDENCIES_ONLY "Don't build OSPRay itself, only its dependencies option(BUILD_OSPRAY_CI_EXTRAS "Enable OSPRay's extra compiler checks." OFF) option(BUILD_OSPRAY_APPS "Enable building OSPRay apps" ON) +option(BUILD_OSPRAY_MODULE_CPU "Enable building module_cpu" ON) option(BUILD_OSPRAY_MODULE_MPI "Enable building module_mpi" OFF) option(BUILD_OSPRAY_MODULE_MULTIDEVICE "Enable building module_multidevice" OFF) @@ -213,7 +214,7 @@ endif() ## Build main projects ## set(DEFAULT_BUILD_COMMAND ${CMAKE_COMMAND} --build . --config ${CMAKE_BUILD_TYPE} ${PARALLEL_JOBS_OPTS}) -if (BUILD_OIDN OR BUILD_OSPRAY_MODULE_MPI) +if (BUILD_OIDN OR BUILD_OSPRAY_MODULE_CPU OR BUILD_OSPRAY_MODULE_MPI) set(OSPRAY_ENABLE_MODULES ON) endif() From e7ab776b763042e9310c56b03e876327277e1436 Mon Sep 17 00:00:00 2001 From: Roba Binyahib Date: Mon, 6 Dec 2021 15:30:23 -0700 Subject: [PATCH 05/26] fixing math bug --- modules/mpi/ospray/render/distributed/DistributedRaycast.ispc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mpi/ospray/render/distributed/DistributedRaycast.ispc b/modules/mpi/ospray/render/distributed/DistributedRaycast.ispc index 60f26069a6..0800f6e962 100644 --- a/modules/mpi/ospray/render/distributed/DistributedRaycast.ispc +++ b/modules/mpi/ospray/render/distributed/DistributedRaycast.ispc @@ -152,7 +152,7 @@ vec4f DRR_shadeSurface(const DistributedRaycastRenderer *uniform self, const OBJ *mat = (const OBJ *)dg.material; if (mat) { foreach_unique (m in mat) { - surfaceColor = m->Kd; + surfaceColor = surfaceColor * m->Kd; opacity = m->d; if (valid(m->KdMap)) { vec4f kdFromMap = get4f(m->KdMap, dg); From 3475ae11666ac5c1cd2847224774e21f323de5fb Mon Sep 17 00:00:00 2001 From: Miroslaw Pawlowski Date: Wed, 27 Oct 2021 12:23:09 +0200 Subject: [PATCH 06/26] (part 1 of 2) introduced Shared Struct - a new way of sharing data with ISPC --- modules/cpu/CMakeLists.txt | 5 +- modules/cpu/common/Data.h | 17 +- modules/cpu/common/Group.cpp | 2 +- modules/cpu/common/StructShared.h | 95 ++++ modules/cpu/geometry/GeometricModel.cpp | 2 +- modules/cpu/geometry/Isosurfaces.cpp | 2 +- modules/cpu/ispc_symbols.txt | 1 - modules/cpu/lights/HDRILight.cpp | 4 +- modules/cpu/lights/SunSkyLight.cpp | 10 +- modules/cpu/lights/SunSkyLight.h | 3 +- modules/cpu/math/spectrum.h | 12 +- modules/cpu/render/Material.cpp | 21 +- modules/cpu/render/Material.h | 15 +- modules/cpu/render/Material.ih | 68 ++- modules/cpu/render/Material.ispc | 31 ++ modules/cpu/render/MaterialShared.h | 90 ++++ modules/cpu/render/MaterialType.ih | 18 - modules/cpu/render/Renderer.cpp | 6 +- modules/cpu/render/Renderer.ih | 3 +- modules/cpu/render/ao/surfaces.ispc | 2 +- modules/cpu/render/bsdfs/Lambert.ih | 3 +- modules/cpu/render/materials/Alloy.cpp | 18 +- modules/cpu/render/materials/Alloy.h | 6 +- modules/cpu/render/materials/Alloy.ispc | 52 +-- modules/cpu/render/materials/AlloyShared.h | 30 ++ modules/cpu/render/materials/CarPaint.cpp | 82 ++-- modules/cpu/render/materials/CarPaint.h | 6 +- modules/cpu/render/materials/CarPaint.ispc | 226 +--------- modules/cpu/render/materials/CarPaintShared.h | 100 +++++ modules/cpu/render/materials/Glass.cpp | 21 +- modules/cpu/render/materials/Glass.h | 6 +- modules/cpu/render/materials/Glass.ispc | 51 +-- modules/cpu/render/materials/GlassShared.h | 23 + modules/cpu/render/materials/Luminous.cpp | 8 +- modules/cpu/render/materials/Luminous.h | 6 +- modules/cpu/render/materials/Luminous.ispc | 36 +- modules/cpu/render/materials/LuminousShared.h | 24 ++ modules/cpu/render/materials/Material.ih | 117 ----- modules/cpu/render/materials/Material.ispc | 43 -- modules/cpu/render/materials/Medium.ih | 10 +- modules/cpu/render/materials/MediumShared.h | 22 + modules/cpu/render/materials/Metal.cpp | 39 +- modules/cpu/render/materials/Metal.h | 7 +- modules/cpu/render/materials/Metal.ispc | 60 +-- modules/cpu/render/materials/MetalShared.h | 38 ++ .../cpu/render/materials/MetallicPaint.cpp | 17 +- modules/cpu/render/materials/MetallicPaint.h | 7 +- .../cpu/render/materials/MetallicPaint.ispc | 50 +-- .../render/materials/MetallicPaintShared.h | 36 ++ modules/cpu/render/materials/Mix.cpp | 20 +- modules/cpu/render/materials/Mix.h | 6 +- modules/cpu/render/materials/Mix.ispc | 48 +-- modules/cpu/render/materials/MixShared.h | 28 ++ modules/cpu/render/materials/OBJ.cpp | 27 +- modules/cpu/render/materials/OBJ.h | 6 +- modules/cpu/render/materials/OBJ.ih | 20 +- modules/cpu/render/materials/OBJ.ispc | 57 +-- modules/cpu/render/materials/OBJShared.h | 36 ++ modules/cpu/render/materials/Plastic.cpp | 8 +- modules/cpu/render/materials/Plastic.h | 6 +- modules/cpu/render/materials/Plastic.ispc | 44 +- modules/cpu/render/materials/PlasticShared.h | 26 ++ modules/cpu/render/materials/Principled.cpp | 137 +++--- modules/cpu/render/materials/Principled.h | 6 +- modules/cpu/render/materials/Principled.ispc | 405 ++---------------- .../cpu/render/materials/PrincipledShared.h | 163 +++++++ modules/cpu/render/materials/ThinGlass.cpp | 17 +- modules/cpu/render/materials/ThinGlass.h | 6 +- modules/cpu/render/materials/ThinGlass.ispc | 45 +- .../cpu/render/materials/ThinGlassShared.h | 31 ++ modules/cpu/render/materials/Velvet.cpp | 18 +- modules/cpu/render/materials/Velvet.h | 6 +- modules/cpu/render/materials/Velvet.ispc | 48 +-- modules/cpu/render/materials/VelvetShared.h | 41 ++ .../cpu/render/pathtracer/GeometryLight.ispc | 12 +- .../cpu/render/pathtracer/PathSampler.ispc | 9 +- modules/cpu/render/pathtracer/PathTracer.cpp | 7 +- modules/cpu/render/pathtracer/PathTracer.ispc | 3 +- .../render/pathtracer/TransparentShadow.ispc | 5 +- modules/cpu/render/scivis/surfaces.ih | 2 +- modules/cpu/texture/Texture.cpp | 2 +- modules/cpu/texture/Texture.h | 8 +- modules/cpu/texture/Texture.ih | 103 +++++ modules/cpu/texture/Texture2D.cpp | 35 +- modules/cpu/texture/Texture2D.h | 10 +- modules/cpu/texture/Texture2D.ih | 88 +--- modules/cpu/texture/Texture2D.ispc | 36 +- modules/cpu/texture/Texture2DShared.h | 33 ++ modules/cpu/texture/TextureParam.ih | 31 +- modules/cpu/texture/TextureParam.ispc | 16 - modules/cpu/texture/TextureParamShared.h | 38 ++ modules/cpu/texture/TextureShared.h | 33 ++ modules/cpu/texture/TextureVolume.cpp | 33 +- modules/cpu/texture/TextureVolume.h | 13 +- modules/cpu/texture/TextureVolume.ispc | 61 +-- modules/cpu/texture/TextureVolumeShared.h | 30 ++ modules/cpu/volume/Volume.cpp | 23 +- modules/cpu/volume/Volume.h | 9 +- modules/cpu/volume/Volume.ih | 37 +- modules/cpu/volume/Volume.ispc | 61 +-- modules/cpu/volume/VolumeShared.h | 30 ++ modules/cpu/volume/VolumetricModel.cpp | 31 +- modules/cpu/volume/VolumetricModel.h | 13 +- modules/cpu/volume/VolumetricModel.ih | 19 +- modules/cpu/volume/VolumetricModel.ispc | 39 -- modules/cpu/volume/VolumetricModelShared.h | 36 ++ .../LinearTransferFunction.cpp | 60 ++- .../transferFunction/LinearTransferFunction.h | 10 +- .../LinearTransferFunction.ih | 15 +- .../LinearTransferFunction.ispc | 80 +--- .../LinearTransferFunctionShared.h | 28 ++ .../transferFunction/TransferFunction.cpp | 5 +- .../transferFunction/TransferFunction.h | 9 +- .../transferFunction/TransferFunction.ih | 24 +- .../transferFunction/TransferFunction.ispc | 12 - .../transferFunction/TransferFunctionShared.h | 29 ++ 116 files changed, 1939 insertions(+), 2045 deletions(-) create mode 100644 modules/cpu/common/StructShared.h create mode 100644 modules/cpu/render/Material.ispc create mode 100644 modules/cpu/render/MaterialShared.h delete mode 100644 modules/cpu/render/MaterialType.ih create mode 100644 modules/cpu/render/materials/AlloyShared.h create mode 100644 modules/cpu/render/materials/CarPaintShared.h create mode 100644 modules/cpu/render/materials/GlassShared.h create mode 100644 modules/cpu/render/materials/LuminousShared.h delete mode 100644 modules/cpu/render/materials/Material.ih delete mode 100644 modules/cpu/render/materials/Material.ispc create mode 100644 modules/cpu/render/materials/MediumShared.h create mode 100644 modules/cpu/render/materials/MetalShared.h create mode 100644 modules/cpu/render/materials/MetallicPaintShared.h create mode 100644 modules/cpu/render/materials/MixShared.h create mode 100644 modules/cpu/render/materials/OBJShared.h create mode 100644 modules/cpu/render/materials/PlasticShared.h create mode 100644 modules/cpu/render/materials/PrincipledShared.h create mode 100644 modules/cpu/render/materials/ThinGlassShared.h create mode 100644 modules/cpu/render/materials/VelvetShared.h create mode 100644 modules/cpu/texture/Texture.ih create mode 100644 modules/cpu/texture/Texture2DShared.h delete mode 100644 modules/cpu/texture/TextureParam.ispc create mode 100644 modules/cpu/texture/TextureParamShared.h create mode 100644 modules/cpu/texture/TextureShared.h create mode 100644 modules/cpu/texture/TextureVolumeShared.h create mode 100644 modules/cpu/volume/VolumeShared.h delete mode 100644 modules/cpu/volume/VolumetricModel.ispc create mode 100644 modules/cpu/volume/VolumetricModelShared.h create mode 100644 modules/cpu/volume/transferFunction/LinearTransferFunctionShared.h delete mode 100644 modules/cpu/volume/transferFunction/TransferFunction.ispc create mode 100644 modules/cpu/volume/transferFunction/TransferFunctionShared.h diff --git a/modules/cpu/CMakeLists.txt b/modules/cpu/CMakeLists.txt index 302bd4418c..96b2b28e1e 100644 --- a/modules/cpu/CMakeLists.txt +++ b/modules/cpu/CMakeLists.txt @@ -130,6 +130,7 @@ set(OSPRAY_ISPC_DEVICE_SOURCES render/LoadBalancer.cpp render/Material.cpp + render/Material.ispc render/Renderer.ispc render/Renderer.cpp render/util.ispc @@ -160,7 +161,6 @@ set(OSPRAY_ISPC_DEVICE_SOURCES render/pathtracer/volumes/VolumeSampler.ispc render/bsdfs/MicrofacetAlbedoTables.ispc - render/materials/Material.ispc render/materials/OBJ.ispc render/materials/OBJ.cpp render/materials/Principled.ispc @@ -191,17 +191,14 @@ set(OSPRAY_ISPC_DEVICE_SOURCES texture/Texture2D.ispc texture/TextureVolume.cpp texture/TextureVolume.ispc - texture/TextureParam.ispc texture/registration.cpp volume/Volume.ispc volume/Volume.cpp - volume/VolumetricModel.ispc volume/VolumetricModel.cpp volume/transferFunction/LinearTransferFunction.ispc volume/transferFunction/LinearTransferFunction.cpp - volume/transferFunction/TransferFunction.ispc volume/transferFunction/TransferFunction.cpp volume/transferFunction/registration.cpp ) diff --git a/modules/cpu/common/Data.h b/modules/cpu/common/Data.h index 31391171d7..4f1426fb43 100644 --- a/modules/cpu/common/Data.h +++ b/modules/cpu/common/Data.h @@ -4,7 +4,8 @@ #pragma once #include -#include "./Managed.h" +#include "Managed.h" +#include "StructShared.h" // including "Data_ispc.h" breaks app code using SDK headers #ifndef __ISPC_STRUCT_Data1D__ @@ -354,10 +355,24 @@ inline typename std::enable_if::value, createArrayOfIE(const DataT &data) { std::vector retval; + retval.reserve(data.size()); for (auto &&obj : data) retval.push_back(obj->getIE()); return retval; } + +template +std::vector createArrayOfSh(const DataT &data) +{ + std::vector retval; + retval.reserve(data.size()); + + for (auto &&obj : data) + retval.push_back(obj->getSh()); + + return retval; +} + } // namespace ospray diff --git a/modules/cpu/common/Group.cpp b/modules/cpu/common/Group.cpp index 27b4286b99..fe90bb5b48 100644 --- a/modules/cpu/common/Group.cpp +++ b/modules/cpu/common/Group.cpp @@ -114,7 +114,7 @@ void Group::commit() sceneVolumes = rtcNewScene(embreeDevice); createEmbreeScene(sceneVolumes, *volumetricModels, sceneFlags); - volumetricModelIEs = createArrayOfIE(*volumetricModels); + volumetricModelIEs = createArrayOfSh(*volumetricModels); rtcCommitScene(sceneVolumes); } diff --git a/modules/cpu/common/StructShared.h b/modules/cpu/common/StructShared.h new file mode 100644 index 0000000000..ad6c2d3e96 --- /dev/null +++ b/modules/cpu/common/StructShared.h @@ -0,0 +1,95 @@ +// Copyright 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include +#include + +namespace ospray { + +/* + Usage: + derive from AddStructShared + + We use multiple inheritance with a virtual base class, thus only a single + instance of structSharedPtr is present, which will be initialized first. + StructSharedGet adds getSh returning the correctly typed pointer. It is + derived from first to handle the memory allocation with the maximum size of + the final StructShared. StructSharedGet does not have any data to ensure + final classes can be C-style casted to ManagedObject* (this pointer stays + the same). +*/ + +struct StructSharedPtr +{ + ~StructSharedPtr(); + + template + friend struct StructSharedGet; + + template + friend struct AddStructShared; + + private: + void *structSharedPtr{nullptr}; +}; + +template +struct StructSharedGet +{ + StructSharedGet(void **); + T *getSh() const; +}; + +// AddStructShared //////////////////////////////////////////// + +template +struct AddStructShared + : public StructSharedGet>, + public Base, + public virtual StructSharedPtr +{ + // using StructShared_t = Struct; + using StructSharedGet>::getSh; + AddStructShared() + : StructSharedGet>(&structSharedPtr) + {} +}; + +// Inlined definitions //////////////////////////////////////// + +inline StructSharedPtr::~StructSharedPtr() +{ + free(structSharedPtr); +} + +template +StructSharedGet::StructSharedGet(void **ptr) +{ + if (!*ptr) + *ptr = new (malloc(sizeof(T))) T; +} + +template +T *StructSharedGet::getSh() const +{ + return static_cast(static_cast(this)->structSharedPtr); +} + +// Testing //////////////////////////////////////////////////// +namespace test { +// clang-format off +struct A {}; +struct B { char b; }; +struct D1 { B super; }; +struct D2 { D1 super; char c; }; + +struct ShouldPass1 : public AddStructShared {}; +struct ShouldPass2 : public AddStructShared {}; +struct ShouldPass3 : public AddStructShared {}; +struct ShouldPass4 : public AddStructShared {}; +// clang-format on +} // namespace test + +} // namespace ospray diff --git a/modules/cpu/geometry/GeometricModel.cpp b/modules/cpu/geometry/GeometricModel.cpp index 96dd8dbef4..b9ad582197 100644 --- a/modules/cpu/geometry/GeometricModel.cpp +++ b/modules/cpu/geometry/GeometricModel.cpp @@ -35,7 +35,7 @@ void GeometricModel::commit() bool useRendererMaterialList = false; materialData = getParamDataT("material", false, true); if (materialData) { - ispcMaterialPtrs = createArrayOfIE(materialData->as()); + ispcMaterialPtrs = createArrayOfSh(materialData->as()); auto *data = new Data(ispcMaterialPtrs.data(), OSP_VOID_PTR, diff --git a/modules/cpu/geometry/Isosurfaces.cpp b/modules/cpu/geometry/Isosurfaces.cpp index 55a01fc353..8277914a2b 100644 --- a/modules/cpu/geometry/Isosurfaces.cpp +++ b/modules/cpu/geometry/Isosurfaces.cpp @@ -91,7 +91,7 @@ void Isosurfaces::commit() isovaluesData->size(), isovaluesData->data(), model ? model->getIE() : nullptr, - volume ? volume->getIE() : nullptr, + volume ? volume->getSh() : nullptr, vklHitContext); postCreationInfo(); diff --git a/modules/cpu/ispc_symbols.txt b/modules/cpu/ispc_symbols.txt index 0017fb0d96..360dbd206e 100644 --- a/modules/cpu/ispc_symbols.txt +++ b/modules/cpu/ispc_symbols.txt @@ -9,7 +9,6 @@ Light_eval___un_3C_s_5B__c_unLight_5D__3E_REFs_5B__c_vyDifferentialGeometry_5D_R Renderer_Constructor___un_3C_s_5B_unRenderer_5D__3E_, Renderer_getBackground___un_3C_s_5B__c_unRenderer_5D__3E_REFs_5B__c_vyvec2f_5D_, Renderer_getMaxDepth___un_3C_s_5B__c_unRenderer_5D__3E_REFs_5B__c_vyvec2f_5D_, -Volume_Constructor___un_3C_s_5B_unVolume_5D__3E_, World_Constructor___un_3C_s_5B_unWorld_5D__3E_, clippingIntersectionFilterV___UM_un_3C_s_5B__c_unRTCFilterFunctionNArguments_5D__3E_, delete_uniform___un_3C_unv_3E_, diff --git a/modules/cpu/lights/HDRILight.cpp b/modules/cpu/lights/HDRILight.cpp index a5f5be44be..5eba8da223 100644 --- a/modules/cpu/lights/HDRILight.cpp +++ b/modules/cpu/lights/HDRILight.cpp @@ -19,7 +19,7 @@ void *HDRILight::createIE(const void *instance) const ispc::HDRILight_set(ie, (ispc::vec3f &)coloredIntensity, (const ispc::LinearSpace3f &)frame, - map ? map->getIE() : nullptr, + map ? map->getSh() : nullptr, distributionIE); return ie; } @@ -40,7 +40,7 @@ void HDRILight::commit() ispc::HDRILight_destroyDistribution(distributionIE); distributionIE = nullptr; if (map) - distributionIE = ispc::HDRILight_createDistribution(map->getIE()); + distributionIE = ispc::HDRILight_createDistribution(map->getSh()); frame.vx = normalize(-dir); frame.vy = normalize(cross(frame.vx, up)); diff --git a/modules/cpu/lights/SunSkyLight.cpp b/modules/cpu/lights/SunSkyLight.cpp index ee12622403..56a254c1f4 100644 --- a/modules/cpu/lights/SunSkyLight.cpp +++ b/modules/cpu/lights/SunSkyLight.cpp @@ -8,7 +8,7 @@ #include "lights/DirectionalLight_ispc.h" #include "lights/HDRILight_ispc.h" #include "lights/Light_ispc.h" -#include "texture/Texture2D_ispc.h" +#include "texture/Texture2D.h" namespace ospray { @@ -20,13 +20,11 @@ SunSkyLight::SunSkyLight() static auto format = static_cast(OSP_TEXTURE_RGB32F); static auto filter = static_cast(OSP_TEXTURE_FILTER_BILINEAR); - mapIE = ispc::Texture2D_create( - (ispc::vec2i &)skySize, skyImage.data(), format, filter); + mapIE.Set(skySize, skyImage.data(), format, filter); } SunSkyLight::~SunSkyLight() { - ispc::delete_uniform(mapIE); ispc::HDRILight_destroyDistribution(distributionIE); } @@ -37,7 +35,7 @@ void *SunSkyLight::createIE(const void *instance) const ispc::HDRILight_set(ie, (ispc::vec3f &)coloredIntensity, (const ispc::LinearSpace3f &)frame, - mapIE, + &mapIE, distributionIE); return ie; } @@ -165,7 +163,7 @@ void SunSkyLight::commit() // recreate distribution ispc::HDRILight_destroyDistribution(distributionIE); - distributionIE = ispc::HDRILight_createDistribution(mapIE); + distributionIE = ispc::HDRILight_createDistribution(&mapIE); } void SunSkyLight::processIntensityQuantityType() diff --git a/modules/cpu/lights/SunSkyLight.h b/modules/cpu/lights/SunSkyLight.h index 4e7cf4f842..1af11cb658 100644 --- a/modules/cpu/lights/SunSkyLight.h +++ b/modules/cpu/lights/SunSkyLight.h @@ -8,6 +8,7 @@ #include "rkcommon/tasking/parallel_for.h" #include "sky_model/color_info.h" #include "sky_model/sky_model.h" +#include "texture/Texture2DShared.h" // Sun and sky environment lights // [Hosek and Wilkie 2012, "An Analytic Model for Full Spectral Sky-Dome @@ -45,7 +46,7 @@ struct OSPRAY_SDK_INTERFACE SunSkyLight : public Light void processIntensityQuantityType(); std::vector skyImage; - void *mapIE{nullptr}; + ispc::Texture2D mapIE; void *distributionIE{nullptr}; vec2i skySize; linear3f frame{one}; // sky orientation diff --git a/modules/cpu/math/spectrum.h b/modules/cpu/math/spectrum.h index 6e33761a4d..46958fc5a6 100644 --- a/modules/cpu/math/spectrum.h +++ b/modules/cpu/math/spectrum.h @@ -1,12 +1,20 @@ -// Copyright 2009-2019 Intel Corporation +// Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once +// ==> 430..675 #define SPECTRUM_SAMPLES 8 #define SPECTRUM_FIRSTWL 430.f #define SPECTRUM_SPACING 35.f -// ==> 430..675 + +#ifdef __cplusplus +#include +struct alignas(SPECTRUM_SAMPLES * sizeof(float)) spectrum + : public std::array +{ +}; +#endif // __cplusplus #define SPECTRUM_AL_ETA \ { \ diff --git a/modules/cpu/render/Material.cpp b/modules/cpu/render/Material.cpp index 77ae1ca9e6..5befc4583b 100644 --- a/modules/cpu/render/Material.cpp +++ b/modules/cpu/render/Material.cpp @@ -4,9 +4,9 @@ // ospray #include "Material.h" #include "common/Util.h" -#include "texture/Texture2D.h" - -#include "texture/TextureParam_ispc.h" +#include "texture/Texture.h" +// ispc +#include "render/Material_ispc.h" namespace ospray { @@ -17,10 +17,12 @@ static FactoryMap g_materialsMap; Material::Material() { managedObjectType = OSP_MATERIAL; + getSh()->getTransparency = ispc::Material_getTransparency_addr(); + getSh()->selectNextMedium = ispc::Material_selectNextMedium_addr(); } Material *Material::createInstance( - const char */*ignored*/, const char *_material_type) + const char * /*ignored*/, const char *_material_type) { std::string name = _material_type; return createInstanceHelper(name, g_materialsMap[name]); @@ -41,7 +43,7 @@ void Material::commit() {} ispc::TextureParam Material::getTextureParam(const char *texture_name) { // Get texture pointer - Texture2D *ptr = (Texture2D *)getParamObject(texture_name); + Texture *ptr = (Texture *)getParamObject(texture_name); // Get 2D transformation if exists int transformFlags = ispc::TRANSFORM_FLAG_NONE; @@ -63,11 +65,10 @@ ispc::TextureParam Material::getTextureParam(const char *texture_name) // Initialize ISPC structure ispc::TextureParam param; - TextureParam_set(¶m, - ptr ? ptr->getIE() : nullptr, - (ispc::TransformFlags)transformFlags, - (const ispc::AffineSpace2f &)xfm2f, - (const ispc::AffineSpace3f &)xfm3f); + param.ptr = ptr ? ptr->getSh() : nullptr; + param.transformFlags = (ispc::TransformFlags)transformFlags; + param.xform2f = xfm2f; + param.xform3f = xfm3f; // Done return param; diff --git a/modules/cpu/render/Material.h b/modules/cpu/render/Material.h index 3929ebbc2a..c55de9f7b2 100644 --- a/modules/cpu/render/Material.h +++ b/modules/cpu/render/Material.h @@ -4,8 +4,11 @@ #pragma once #include "common/Managed.h" +#include "common/StructShared.h" #include "common/Util.h" -#include "texture/TextureParam_ispc.h" + +#include "MaterialShared.h" +#include "texture/TextureParamShared.h" namespace ospray { @@ -21,7 +24,8 @@ struct MaterialParam using MaterialParam1f = MaterialParam; using MaterialParam3f = MaterialParam; -struct OSPRAY_SDK_INTERFACE Material : public ManagedObject +struct OSPRAY_SDK_INTERFACE Material + : public AddStructShared { Material(); virtual ~Material() override = default; @@ -42,10 +46,9 @@ struct OSPRAY_SDK_INTERFACE Material : public ManagedObject material types specified in special modules, make sure to call ospLoadModule first. */ static Material *createInstance( - const char */*ignored*/, const char *material_type); + const char * /*ignored*/, const char *material_type); template - static void registerType( - const char */*ignored*/, const char *material_type); + static void registerType(const char * /*ignored*/, const char *material_type); private: template @@ -79,7 +82,7 @@ OSPTYPEFOR_SPECIALIZATION(Material *, OSP_MATERIAL); template inline void Material::registerType( - const char */*ignored*/, const char *material_type) + const char * /*ignored*/, const char *material_type) { std::string name(material_type); diff --git a/modules/cpu/render/Material.ih b/modules/cpu/render/Material.ih index aea0187e40..84f27d0625 100644 --- a/modules/cpu/render/Material.ih +++ b/modules/cpu/render/Material.ih @@ -3,10 +3,68 @@ #pragma once -#include "MaterialType.ih" +#include "common/DifferentialGeometry.ih" +#include "common/Ray.ih" +#include "render/MaterialShared.h" +#include "render/bsdfs/BSDF.ih" +#include "render/bsdfs/ShadingContext.ih" +#include "texture/TextureParam.ih" -/*! ISPC-side abstraction for a material. */ -struct Material +#include "MaterialShared.h" + +inline linear3f makeShadingFrame(const DifferentialGeometry &dg) +{ + vec3f N = dg.Ns; + vec3f U = normalize(dg.dPds); + vec3f V = cross(N, U); + if (dot(V, V) > 0.f) { + V = normalize(V); + U = cross(V, N); + return make_LinearSpace3f(U, V, N); + } else { + return frame(N); + } +} + +inline linear3f makeShadingFrame(const linear3f &f, const vec3f &localN) +{ + // in general f is not ortho-normal, thus need to re-normalize + vec3f N = normalize(f * localN); // transform normal to world space + vec3f U = f.vx; + vec3f V = cross(N, U); + if (dot(V, V) > 0.f) { + V = normalize(V); + U = cross(V, N); + return make_LinearSpace3f(U, V, N); + } else { + return frame(N); + } +} + +inline linear3f makeShadingFrame( + const DifferentialGeometry &dg, const vec3f &localN) +{ + linear3f f = makeShadingFrame(dg); + return makeShadingFrame(f, localN); +} + +inline linear3f makeShadingFrame(const DifferentialGeometry &dg, + const uniform TextureParam &normalMap, + const uniform linear2f &normalRot, + uniform float normalScale = 1.f) { - uniform MaterialType type; -}; + if (valid(normalMap)) { + linear3f f = + make_LinearSpace3f(normalize(dg.dPds), normalize(dg.dPdt), dg.Ns); + // get normal from texture + vec3f localN = + getNormal(normalMap, dg) * make_vec3f(normalScale, normalScale, 1.f); + // rotate in 2D (tangent space) to account for tc transformations + vec2f rotN = normalRot * make_vec2f(localN.x, localN.y); + localN.x = rotN.x; + localN.y = rotN.y; + return makeShadingFrame(f, localN); + } else { + return makeShadingFrame(dg); + } +} diff --git a/modules/cpu/render/Material.ispc b/modules/cpu/render/Material.ispc new file mode 100644 index 0000000000..d11987bcb2 --- /dev/null +++ b/modules/cpu/render/Material.ispc @@ -0,0 +1,31 @@ +// Copyright 2009-2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#include "Material.ih" + +vec3f Material_getTransparency(const uniform Material *uniform self, + const DifferentialGeometry &dg, + const Ray &ray, + const Medium ¤tMedium) +{ + return make_vec3f(0.0f); +} + +void Material_selectNextMedium(const uniform Material *uniform self, + const DifferentialGeometry &dg, + Medium ¤tMedium) +{ /* do nothing by default */ +} + +/////////////////////////////////////////////////////////////////////////////// +// External API + +export void *uniform Material_getTransparency_addr() +{ + return Material_getTransparency; +} + +export void *uniform Material_selectNextMedium_addr() +{ + return Material_selectNextMedium; +} diff --git a/modules/cpu/render/MaterialShared.h b/modules/cpu/render/MaterialShared.h new file mode 100644 index 0000000000..de1e357312 --- /dev/null +++ b/modules/cpu/render/MaterialShared.h @@ -0,0 +1,90 @@ +// Copyright 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#define EPS 1e-5f + +#ifdef __cplusplus +using namespace rkcommon::math; +namespace ispc { +typedef void *Material_GetBSDFFunc; +typedef void *Material_GetTransparencyFunc; +typedef void *Material_SelectNextMediumFunc; +#else +struct BSDF; +struct ShadingContext; +struct Medium; +struct DifferentialGeometry; +struct Ray; +struct Material; + +typedef const varying BSDF *varying (*Material_GetBSDFFunc)( + const uniform Material *uniform self, + uniform ShadingContext *uniform ctx, + // The point to shade on a surface. + const DifferentialGeometry &dg, + // The ray arriving at the point to shade. + const Ray &ray, + // The medium this ray travels inside. + const Medium ¤tMedium); + +// shortcut: compute transmission of material, for transparent shadows, +// neglecting refraction +typedef vec3f (*Material_GetTransparencyFunc)( + const uniform Material *uniform self, + // The point to shade on a surface. + const DifferentialGeometry &dg, + // The ray arriving at the point to shade. + const Ray &ray, + // The medium this ray travels inside. + const Medium ¤tMedium); + +typedef void (*Material_SelectNextMediumFunc)( + const uniform Material *uniform self, + const DifferentialGeometry &dg, + Medium ¤tMedium); +#endif // __cplusplus + +enum MaterialType +{ + MATERIAL_TYPE_ALLOY = 0, + MATERIAL_TYPE_CARPAINT = 1, + MATERIAL_TYPE_GLASS = 2, + MATERIAL_TYPE_LUMINOUS = 3, + MATERIAL_TYPE_METAL = 4, + MATERIAL_TYPE_METALLICPAINT = 5, + MATERIAL_TYPE_MIX = 6, + MATERIAL_TYPE_OBJ = 7, + MATERIAL_TYPE_PLASTIC = 8, + MATERIAL_TYPE_PRINCIPLED = 9, + MATERIAL_TYPE_THINGLASS = 10, + MATERIAL_TYPE_VELVET = 11 +}; + +// ISPC-side abstraction for a material. +struct Material +{ + MaterialType type; + Material_GetBSDFFunc getBSDF; + Material_GetTransparencyFunc getTransparency; + Material_SelectNextMediumFunc selectNextMedium; + vec3f emission; // simple constant (spatially and angular) emission, returns + // radiance; TODO SV-EDFs +#ifdef __cplusplus + Material(const vec3f &emission = vec3f(0.f)) + : getBSDF(nullptr), + getTransparency(nullptr), + selectNextMedium(nullptr), + emission(emission) + {} + + bool isEmissive() + { + return reduce_max(emission) > 0.f; + } +}; +} // namespace ispc +#else +}; +#endif // __cplusplus diff --git a/modules/cpu/render/MaterialType.ih b/modules/cpu/render/MaterialType.ih deleted file mode 100644 index 835bb4fce2..0000000000 --- a/modules/cpu/render/MaterialType.ih +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2020-2021 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -typedef int MaterialType; -#define MATERIAL_TYPE_ALLOY 0 -#define MATERIAL_TYPE_CARPAINT 1 -#define MATERIAL_TYPE_GLASS 2 -#define MATERIAL_TYPE_LUMINOUS 3 -#define MATERIAL_TYPE_METAL 4 -#define MATERIAL_TYPE_METALLICPAINT 5 -#define MATERIAL_TYPE_MIX 6 -#define MATERIAL_TYPE_OBJ 7 -#define MATERIAL_TYPE_PLASTIC 8 -#define MATERIAL_TYPE_PRINCIPLED 9 -#define MATERIAL_TYPE_THINGLASS 10 -#define MATERIAL_TYPE_VELVET 11 diff --git a/modules/cpu/render/Renderer.cpp b/modules/cpu/render/Renderer.cpp index cf286c69c0..5c6f1a91d2 100644 --- a/modules/cpu/render/Renderer.cpp +++ b/modules/cpu/render/Renderer.cpp @@ -57,7 +57,7 @@ void Renderer::commit() setupPixelFilter(); if (materialData) - ispcMaterialPtrs = createArrayOfIE(*materialData); + ispcMaterialPtrs = createArrayOfSh(*materialData); else ispcMaterialPtrs.clear(); @@ -67,10 +67,10 @@ void Renderer::commit() maxDepth, minContribution, (ispc::vec4f &)bgColor, - backplate ? backplate->getIE() : nullptr, + backplate ? backplate->getSh() : nullptr, ispcMaterialPtrs.size(), ispcMaterialPtrs.data(), - maxDepthTexture ? maxDepthTexture->getIE() : nullptr, + maxDepthTexture ? maxDepthTexture->getSh() : nullptr, pixelFilter ? pixelFilter->getIE() : nullptr); } } diff --git a/modules/cpu/render/Renderer.ih b/modules/cpu/render/Renderer.ih index ca75c5ffb3..290ab85bf2 100644 --- a/modules/cpu/render/Renderer.ih +++ b/modules/cpu/render/Renderer.ih @@ -3,13 +3,14 @@ #pragma once -#include "Material.ih" #include "camera/Camera.ih" #include "fb/FrameBuffer.ih" #include "fb/Tile.ih" #include "pf/PixelFilter.ih" #include "texture/Texture2D.ih" +#include "MaterialShared.h" + struct Renderer; struct World; struct Camera; diff --git a/modules/cpu/render/ao/surfaces.ispc b/modules/cpu/render/ao/surfaces.ispc index 9c8bcb6196..5d37f93df8 100644 --- a/modules/cpu/render/ao/surfaces.ispc +++ b/modules/cpu/render/ao/surfaces.ispc @@ -15,7 +15,7 @@ inline vec4f getSurfaceColor(const DifferentialGeometry &dg) vec3f surfaceColor = make_vec3f(1.f); float opacity = 1.f; foreach_unique (m in mat) { - if (m != NULL && m->super.material.type == MATERIAL_TYPE_OBJ) { + if (m != NULL && m->super.type == MATERIAL_TYPE_OBJ) { surfaceColor = m->Kd; if (valid(m->KdMap)) { vec4f Kd_from_map = get4f(m->KdMap, dg); diff --git a/modules/cpu/render/bsdfs/Lambert.ih b/modules/cpu/render/bsdfs/Lambert.ih index a2cb96c80c..74134867e7 100644 --- a/modules/cpu/render/bsdfs/Lambert.ih +++ b/modules/cpu/render/bsdfs/Lambert.ih @@ -1,9 +1,10 @@ -// Copyright 2009-2019 Intel Corporation +// Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once #include "BSDF.ih" +#include "ShadingContext.ih" inline BSDF_EvalRes Lambert_eval( const varying BSDF *uniform self, const vec3f &wo, const vec3f &wi) diff --git a/modules/cpu/render/materials/Alloy.cpp b/modules/cpu/render/materials/Alloy.cpp index debd5cac8a..ff6c641867 100644 --- a/modules/cpu/render/materials/Alloy.cpp +++ b/modules/cpu/render/materials/Alloy.cpp @@ -2,8 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 #include "Alloy.h" -#include "common/Data.h" -#include "math/spectrum.h" // ispc #include "render/materials/Alloy_ispc.h" @@ -12,7 +10,8 @@ namespace pathtracer { Alloy::Alloy() { - ispcEquivalent = ispc::PathTracer_Alloy_create(); + getSh()->super.type = ispc::MATERIAL_TYPE_ALLOY; + getSh()->super.getBSDF = ispc::Alloy_getBSDF_addr(); } std::string Alloy::toString() const @@ -27,13 +26,12 @@ void Alloy::commit() MaterialParam3f edgeColor = getMaterialParam3f("edgeColor", vec3f(1.f)); MaterialParam1f roughness = getMaterialParam1f("roughness", .1f); - ispc::PathTracer_Alloy_set(getIE(), - (const ispc::vec3f &)color.factor, - color.tex, - (const ispc::vec3f &)edgeColor.factor, - edgeColor.tex, - roughness.factor, - roughness.tex); + getSh()->color = color.factor; + getSh()->colorMap = color.tex; + getSh()->edgeColor = edgeColor.factor; + getSh()->edgeColorMap = edgeColor.tex; + getSh()->roughness = roughness.factor; + getSh()->roughnessMap = roughness.tex; } } // namespace pathtracer diff --git a/modules/cpu/render/materials/Alloy.h b/modules/cpu/render/materials/Alloy.h index 813662185c..5fa93e4cd4 100644 --- a/modules/cpu/render/materials/Alloy.h +++ b/modules/cpu/render/materials/Alloy.h @@ -1,12 +1,14 @@ -// Copyright 2020 Intel Corporation +// Copyright 2020-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "render/Material.h" +#include "AlloyShared.h" + namespace ospray { namespace pathtracer { -struct Alloy : public ospray::Material +struct Alloy : public AddStructShared { Alloy(); diff --git a/modules/cpu/render/materials/Alloy.ispc b/modules/cpu/render/materials/Alloy.ispc index 59de40f358..2b6f5326a6 100644 --- a/modules/cpu/render/materials/Alloy.ispc +++ b/modules/cpu/render/materials/Alloy.ispc @@ -1,29 +1,17 @@ // Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 -#include "../bsdfs/Conductor.ih" -#include "../bsdfs/MicrofacetConductor.ih" -#include "Material.ih" -#include "math/spectrum.ih" +#include "render/Material.ih" +#include "render/bsdfs/Conductor.ih" +#include "render/bsdfs/MicrofacetConductor.ih" #include "texture/TextureParam.ih" -struct Alloy -{ - PathTraceMaterial super; - - vec3f color; // reflectivity at normal incidence (0 deg) - TextureParam colorMap; - vec3f edgeColor; // reflectivity at grazing angle (90 deg) - TextureParam edgeColorMap; - float roughness; // in [0, 1]; 0==ideally smooth (mirror) - TextureParam roughnessMap; -}; +#include "AlloyShared.h" /////////////////////////////////////////////////////////////////////////////// // Implementation -const varying BSDF *varying Alloy_getBSDF( - const uniform PathTraceMaterial *uniform super, +const varying BSDF *varying Alloy_getBSDF(const uniform Material *uniform super, uniform ShadingContext *uniform ctx, const DifferentialGeometry &dg, const Ray &ray, @@ -51,33 +39,7 @@ const varying BSDF *varying Alloy_getBSDF( /////////////////////////////////////////////////////////////////////////////// // External API -export void *uniform PathTracer_Alloy_set(void *uniform _self, - const uniform vec3f &color, - const uniform TextureParam &colorMap, - const uniform vec3f &edgeColor, - const uniform TextureParam &edgeColorMap, - uniform float roughness, - const uniform TextureParam &roughnessMap) +export void *uniform Alloy_getBSDF_addr() { - Alloy *uniform self = (Alloy * uniform) _self; - - self->color = color; - self->colorMap = colorMap; - - self->edgeColor = edgeColor; - self->edgeColorMap = edgeColorMap; - - self->roughness = roughness; - self->roughnessMap = roughnessMap; -} - -export void *uniform PathTracer_Alloy_create() -{ - Alloy *uniform self = uniform new Alloy; - PathTraceMaterial_Constructor(&self->super, MATERIAL_TYPE_ALLOY, Alloy_getBSDF); - - uniform TextureParam tex = make_TextureParam(); - PathTracer_Alloy_set( - self, make_vec3f(0.9f), tex, make_vec3f(1.f), tex, 0.1f, tex); - return self; + return Alloy_getBSDF; } diff --git a/modules/cpu/render/materials/AlloyShared.h b/modules/cpu/render/materials/AlloyShared.h new file mode 100644 index 0000000000..56f8acdaca --- /dev/null +++ b/modules/cpu/render/materials/AlloyShared.h @@ -0,0 +1,30 @@ +// Copyright 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "render/MaterialShared.h" +#include "texture/TextureParamShared.h" + +#ifdef __cplusplus +namespace ispc { +#endif // __cplusplus + +struct Alloy +{ + Material super; + + vec3f color; // reflectivity at normal incidence (0 deg) + TextureParam colorMap; + vec3f edgeColor; // reflectivity at grazing angle (90 deg) + TextureParam edgeColorMap; + float roughness; // in [0, 1]; 0==ideally smooth (mirror) + TextureParam roughnessMap; + +#ifdef __cplusplus + Alloy() : color(.9f), edgeColor(1.f), roughness(.1f) {} +}; +} // namespace ispc +#else +}; +#endif // __cplusplus diff --git a/modules/cpu/render/materials/CarPaint.cpp b/modules/cpu/render/materials/CarPaint.cpp index 070ddfe12e..18aa401dfe 100644 --- a/modules/cpu/render/materials/CarPaint.cpp +++ b/modules/cpu/render/materials/CarPaint.cpp @@ -2,8 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 #include "CarPaint.h" -#include "common/Data.h" -#include "math/spectrum.h" // ispc #include "render/materials/CarPaint_ispc.h" @@ -12,7 +10,8 @@ namespace pathtracer { CarPaint::CarPaint() { - ispcEquivalent = ispc::PathTracer_CarPaint_create(); + getSh()->super.type = ispc::MATERIAL_TYPE_CARPAINT; + getSh()->super.getBSDF = ispc::CarPaint_getBSDF_addr(); } std::string CarPaint::toString() const @@ -44,46 +43,43 @@ void CarPaint::commit() getMaterialParam3f("flipflopColor", vec3f(1.f)); MaterialParam1f flipflopFalloff = getMaterialParam1f("flipflopFalloff", 1.f); - ispc::PathTracer_CarPaint_set(getIE(), - (const ispc::vec3f &)baseColor.factor, - baseColor.tex, - roughness.factor, - roughness.tex, - normal.factor, - normal.tex, - (const ispc::LinearSpace2f &)normal.rot, - useFlakeColor, - (const ispc::vec3f &)flakeColor.factor, - flakeColor.tex, - flakeScale.factor, - flakeScale.tex, - flakeDensity.factor, - flakeDensity.tex, - flakeSpread.factor, - flakeSpread.tex, - flakeJitter.factor, - flakeJitter.tex, - flakeRoughness.factor, - flakeRoughness.tex, - - coat.factor, - coat.tex, - coatIor.factor, - coatIor.tex, - (const ispc::vec3f &)coatColor.factor, - coatColor.tex, - coatThickness.factor, - coatThickness.tex, - coatRoughness.factor, - coatRoughness.tex, - coatNormal.factor, - coatNormal.tex, - (const ispc::LinearSpace2f &)coatNormal.rot, - - (const ispc::vec3f &)flipflopColor.factor, - flipflopColor.tex, - flipflopFalloff.factor, - flipflopFalloff.tex); + getSh()->baseColor = baseColor.factor; + getSh()->baseColorMap = baseColor.tex; + getSh()->roughness = roughness.factor; + getSh()->roughnessMap = roughness.tex; + getSh()->normal = normal.factor; + getSh()->normalMap = normal.tex; + getSh()->normalRot = normal.rot; + getSh()->useFlakeColor = useFlakeColor; + getSh()->flakeColor = flakeColor.factor; + getSh()->flakeColorMap = flakeColor.tex; + getSh()->flakeScale = flakeScale.factor; + getSh()->flakeScaleMap = flakeScale.tex; + getSh()->flakeDensity = flakeDensity.factor; + getSh()->flakeDensityMap = flakeDensity.tex; + getSh()->flakeSpread = flakeSpread.factor; + getSh()->flakeSpreadMap = flakeSpread.tex; + getSh()->flakeJitter = flakeJitter.factor; + getSh()->flakeJitterMap = flakeJitter.tex; + getSh()->flakeRoughness = flakeRoughness.factor; + getSh()->flakeRoughnessMap = flakeRoughness.tex; + getSh()->coat = coat.factor; + getSh()->coatMap = coat.tex; + getSh()->coatIor = coatIor.factor; + getSh()->coatIorMap = coatIor.tex; + getSh()->coatColor = coatColor.factor; + getSh()->coatColorMap = coatColor.tex; + getSh()->coatThickness = coatThickness.factor; + getSh()->coatThicknessMap = coatThickness.tex; + getSh()->coatRoughness = coatRoughness.factor; + getSh()->coatRoughnessMap = coatRoughness.tex; + getSh()->coatNormal = coatNormal.factor; + getSh()->coatNormalMap = coatNormal.tex; + getSh()->coatNormalRot = coatNormal.rot; + getSh()->flipflopColor = flipflopColor.factor; + getSh()->flipflopColorMap = flipflopColor.tex; + getSh()->flipflopFalloff = flipflopFalloff.factor; + getSh()->flipflopFalloffMap = flipflopFalloff.tex; } } // namespace pathtracer diff --git a/modules/cpu/render/materials/CarPaint.h b/modules/cpu/render/materials/CarPaint.h index fc435e4c66..7eec4de70a 100644 --- a/modules/cpu/render/materials/CarPaint.h +++ b/modules/cpu/render/materials/CarPaint.h @@ -1,12 +1,14 @@ -// Copyright 2020 Intel Corporation +// Copyright 2020-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "render/Material.h" +#include "CarPaintShared.h" + namespace ospray { namespace pathtracer { -struct CarPaint : public ospray::Material +struct CarPaint : public AddStructShared { CarPaint(); diff --git a/modules/cpu/render/materials/CarPaint.ispc b/modules/cpu/render/materials/CarPaint.ispc index 4e2ac4b256..6518808341 100644 --- a/modules/cpu/render/materials/CarPaint.ispc +++ b/modules/cpu/render/materials/CarPaint.ispc @@ -1,85 +1,24 @@ // Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 -#include "../bsdfs/Conductor.ih" -#include "../bsdfs/DielectricLayer.ih" -#include "../bsdfs/Lambert.ih" -#include "../bsdfs/MicrofacetConductor.ih" -#include "../bsdfs/MicrofacetDielectricLayer.ih" -#include "../bsdfs/OrenNayar.ih" -#include "render/shaders/Flakes.ih" -#include "Material.ih" +#include "render/Material.ih" +#include "render/bsdfs/Conductor.ih" +#include "render/bsdfs/DielectricLayer.ih" +#include "render/bsdfs/Lambert.ih" +#include "render/bsdfs/MicrofacetConductor.ih" +#include "render/bsdfs/MicrofacetDielectricLayer.ih" #include "render/bsdfs/MultiBSDF.ih" +#include "render/bsdfs/OrenNayar.ih" +#include "render/shaders/Flakes.ih" #include "texture/TextureParam.ih" -struct CarPaint -{ - PathTraceMaterial super; - - vec3f baseColor; - TextureParam baseColorMap; - - float roughness; - TextureParam roughnessMap; - - float normal; // scale - TextureParam normalMap; - linear2f normalRot; - - bool useFlakeColor; // if disabled, flakes are Aluminium - vec3f flakeColor; - TextureParam flakeColorMap; - - float flakeScale; - TextureParam flakeScaleMap; - - float flakeDensity; - TextureParam flakeDensityMap; - - float flakeSpread; - TextureParam flakeSpreadMap; - - float flakeJitter; - TextureParam flakeJitterMap; - - float flakeRoughness; - TextureParam flakeRoughnessMap; - - // dielectric clear coat reflectivity in [0, 1] - float coat; - TextureParam coatMap; - - // dielectric clear coat index of refraction in [1, 3] - float coatIor; - TextureParam coatIorMap; - - float coatThickness; - TextureParam coatThicknessMap; - - float coatRoughness; - TextureParam coatRoughnessMap; - - float coatNormal; // scale - TextureParam coatNormalMap; - linear2f coatNormalRot; - - vec3f coatColor; - TextureParam coatColorMap; - - vec3f flipflopColor; - TextureParam flipflopColorMap; - - float flipflopFalloff; - TextureParam flipflopFalloffMap; -}; - -#define EPS 1e-5f +#include "CarPaintShared.h" /////////////////////////////////////////////////////////////////////////////// // Implementation const varying BSDF *varying CarPaint_getBSDF( - const uniform PathTraceMaterial *uniform super, + const uniform Material *uniform super, uniform ShadingContext *uniform ctx, const DifferentialGeometry &dg, const Ray &ray, @@ -217,148 +156,7 @@ const varying BSDF *varying CarPaint_getBSDF( /////////////////////////////////////////////////////////////////////////////// // External API -export void *uniform PathTracer_CarPaint_set(void *uniform _self, - const uniform vec3f &baseColor, - const uniform TextureParam &baseColorMap, - uniform float roughness, - const uniform TextureParam &roughnessMap, - uniform float normal, - const uniform TextureParam &normalMap, - const uniform linear2f &normalRot, - const uniform bool useFlakeColor, - const uniform vec3f &flakeColor, - const uniform TextureParam &flakeColorMap, - uniform float flakeScale, - const uniform TextureParam &flakeScaleMap, - uniform float flakeDensity, - const uniform TextureParam &flakeDensityMap, - uniform float flakeSpread, - const uniform TextureParam &flakeSpreadMap, - uniform float flakeJitter, - const uniform TextureParam &flakeJitterMap, - uniform float flakeRoughness, - const uniform TextureParam &flakeRoughnessMap, - uniform float coat, - const uniform TextureParam &coatMap, - uniform float coatIor, - const uniform TextureParam &coatIorMap, - const uniform vec3f &coatColor, - const uniform TextureParam &coatColorMap, - uniform float coatThickness, - const uniform TextureParam &coatThicknessMap, - uniform float coatRoughness, - const uniform TextureParam &coatRoughnessMap, - uniform float coatNormal, - const uniform TextureParam &coatNormalMap, - const uniform linear2f &coatNormalRot, - const uniform vec3f &flipflopColor, - const uniform TextureParam &flipflopColorMap, - uniform float flipflopFalloff, - const uniform TextureParam &flipflopFalloffMap) -{ - CarPaint *uniform self = (CarPaint * uniform) _self; - - self->baseColor = baseColor; - self->baseColorMap = baseColorMap; - - self->roughness = roughness; - self->roughnessMap = roughnessMap; - - self->normal = normal; - self->normalMap = normalMap; - self->normalRot = normalRot; - - self->useFlakeColor = useFlakeColor; - self->flakeColor = flakeColor; - self->flakeColorMap = flakeColorMap; - - self->flakeScale = flakeScale; - self->flakeScaleMap = flakeScaleMap; - - self->flakeDensity = flakeDensity; - self->flakeDensityMap = flakeDensityMap; - - self->flakeSpread = flakeSpread; - self->flakeSpreadMap = flakeSpreadMap; - - self->flakeJitter = flakeJitter; - self->flakeJitterMap = flakeJitterMap; - - self->flakeRoughness = flakeRoughness; - self->flakeRoughnessMap = flakeRoughnessMap; - - self->coat = coat; - self->coatMap = coatMap; - - self->coatIor = coatIor; - self->coatIorMap = coatIorMap; - - self->coatColor = coatColor; - self->coatColorMap = coatColorMap; - - self->coatThickness = coatThickness; - self->coatThicknessMap = coatThicknessMap; - - self->coatRoughness = coatRoughness; - self->coatRoughnessMap = coatRoughnessMap; - - self->coatNormal = coatNormal; - self->coatNormalMap = coatNormalMap; - self->coatNormalRot = coatNormalRot; - - self->flipflopColor = flipflopColor; - self->flipflopColorMap = flipflopColorMap; - - self->flipflopFalloff = flipflopFalloff; - self->flipflopFalloffMap = flipflopFalloffMap; -} - -export void *uniform PathTracer_CarPaint_create() +export void *uniform CarPaint_getBSDF_addr() { - CarPaint *uniform self = uniform new CarPaint; - - PathTraceMaterial_Constructor(&self->super, MATERIAL_TYPE_CARPAINT, CarPaint_getBSDF); - - uniform TextureParam tex = make_TextureParam(); - PathTracer_CarPaint_set(self, - make_vec3f(0.8f), - tex, // baseColor - 0.f, - tex, // roughness - 1.f, - tex, - make_LinearSpace2f_identity(), // normal - 0, // useFlakeColor - make_vec3f(0.f), - tex, // flakeColor - 0.f, - tex, // flakeScale - 0.f, - tex, // flakeDensity - 0.f, - tex, // flakeSpread - 0.f, - tex, // flakeJitter - 0.f, - tex, // flakeRoughness - 1.f, - tex, // coat - 1.5f, - tex, // coatIor - make_vec3f(1.f), - tex, // coatColor - 1.f, - tex, // coatThickness - 0.f, - tex, // coatRoughness - 1.f, - tex, - make_LinearSpace2f_identity(), // coatNormal - make_vec3f(1.f), - tex, // flipflopColor - 1.f, - tex // flipflopFalloff - ); - - return self; + return CarPaint_getBSDF; } diff --git a/modules/cpu/render/materials/CarPaintShared.h b/modules/cpu/render/materials/CarPaintShared.h new file mode 100644 index 0000000000..ed5106d0a9 --- /dev/null +++ b/modules/cpu/render/materials/CarPaintShared.h @@ -0,0 +1,100 @@ +// Copyright 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "render/MaterialShared.h" +#include "texture/TextureParamShared.h" + +#ifdef __cplusplus +namespace ispc { +#endif // __cplusplus + +struct CarPaint +{ + Material super; + + vec3f baseColor; + TextureParam baseColorMap; + + float roughness; + TextureParam roughnessMap; + + float normal; // scale + TextureParam normalMap; + linear2f normalRot; + + bool useFlakeColor; // if disabled, flakes are Aluminium + vec3f flakeColor; + TextureParam flakeColorMap; + + float flakeScale; + TextureParam flakeScaleMap; + + float flakeDensity; + TextureParam flakeDensityMap; + + float flakeSpread; + TextureParam flakeSpreadMap; + + float flakeJitter; + TextureParam flakeJitterMap; + + float flakeRoughness; + TextureParam flakeRoughnessMap; + + // dielectric clear coat reflectivity in [0, 1] + float coat; + TextureParam coatMap; + + // dielectric clear coat index of refraction in [1, 3] + float coatIor; + TextureParam coatIorMap; + + float coatThickness; + TextureParam coatThicknessMap; + + float coatRoughness; + TextureParam coatRoughnessMap; + + float coatNormal; // scale + TextureParam coatNormalMap; + linear2f coatNormalRot; + + vec3f coatColor; + TextureParam coatColorMap; + + vec3f flipflopColor; + TextureParam flipflopColorMap; + + float flipflopFalloff; + TextureParam flipflopFalloffMap; + +#ifdef __cplusplus + CarPaint() + : baseColor(0.8f), + roughness(0.f), + normal(1.f), + normalRot(one), + useFlakeColor(false), + flakeColor(0.f), + flakeScale(0.f), + flakeDensity(0.f), + flakeSpread(0.f), + flakeJitter(0.f), + flakeRoughness(0.f), + coat(1.f), + coatIor(1.5f), + coatThickness(1.f), + coatRoughness(0.f), + coatNormal(1.f), + coatNormalRot(one), + coatColor(1.f), + flipflopColor(1.f), + flipflopFalloff(1.f) + {} +}; +} // namespace ispc +#else +}; +#endif // __cplusplus diff --git a/modules/cpu/render/materials/Glass.cpp b/modules/cpu/render/materials/Glass.cpp index 9e80238b27..e2acb8f995 100644 --- a/modules/cpu/render/materials/Glass.cpp +++ b/modules/cpu/render/materials/Glass.cpp @@ -10,7 +10,10 @@ namespace pathtracer { Glass::Glass() { - ispcEquivalent = ispc::PathTracer_Glass_create(); + getSh()->super.type = ispc::MATERIAL_TYPE_GLASS; + getSh()->super.getBSDF = ispc::Glass_getBSDF_addr(); + getSh()->super.getTransparency = ispc::Glass_getTransparency_addr(); + getSh()->super.selectNextMedium = ispc::Glass_selectNextMedium_addr(); } std::string Glass::toString() const @@ -35,12 +38,16 @@ void Glass::commit() const float attenuationDistance = getParam("attenuationDistance", 1.0f); - ispc::PathTracer_Glass_set(ispcEquivalent, - etaInside, - (const ispc::vec3f &)attenuationColorInside, - etaOutside, - (const ispc::vec3f &)attenuationColorOutside, - attenuationDistance); + getSh()->mediumInside.ior = etaInside; + getSh()->mediumInside.attenuation = vec3f(log(attenuationColorInside.x), + log(attenuationColorInside.y), + log(attenuationColorInside.z)) + / std::max(attenuationDistance, EPS); + getSh()->mediumOutside.ior = etaOutside; + getSh()->mediumOutside.attenuation = vec3f(log(attenuationColorOutside.x), + log(attenuationColorOutside.y), + log(attenuationColorOutside.z)) + / std::max(attenuationDistance, EPS); } } // namespace pathtracer diff --git a/modules/cpu/render/materials/Glass.h b/modules/cpu/render/materials/Glass.h index 758eec62e3..a02b033054 100644 --- a/modules/cpu/render/materials/Glass.h +++ b/modules/cpu/render/materials/Glass.h @@ -1,12 +1,14 @@ -// Copyright 2020 Intel Corporation +// Copyright 2020-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "render/Material.h" +#include "GlassShared.h" + namespace ospray { namespace pathtracer { -struct Glass : public ospray::Material +struct Glass : public AddStructShared { Glass(); diff --git a/modules/cpu/render/materials/Glass.ispc b/modules/cpu/render/materials/Glass.ispc index 1c47da1cfc..184d1de423 100644 --- a/modules/cpu/render/materials/Glass.ispc +++ b/modules/cpu/render/materials/Glass.ispc @@ -1,23 +1,16 @@ // Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 -#include "Material.ih" +#include "Medium.ih" +#include "render/Material.ih" #include "render/bsdfs/RobustDielectric.ih" -#include "texture/TextureParam.ih" -struct Glass -{ - PathTraceMaterial super; - - Medium mediumInside; - Medium mediumOutside; -}; +#include "GlassShared.h" /////////////////////////////////////////////////////////////////////////////// // Implementation -const varying BSDF *varying Glass_getBSDF( - const uniform PathTraceMaterial *uniform super, +const varying BSDF *varying Glass_getBSDF(const uniform Material *uniform super, uniform ShadingContext *uniform ctx, const DifferentialGeometry &dg, const Ray &ray, @@ -33,7 +26,7 @@ const varying BSDF *varying Glass_getBSDF( return bsdf; } -vec3f Glass_getTransparency(const uniform PathTraceMaterial *uniform material, +vec3f Glass_getTransparency(const uniform Material *uniform material, const DifferentialGeometry &dg, const Ray &ray, const Medium ¤tMedium) @@ -48,7 +41,7 @@ vec3f Glass_getTransparency(const uniform PathTraceMaterial *uniform material, return make_vec3f(1.0f - fresnelDielectric(cosThetaO, eta)); } -void Glass_selectNextMedium(const uniform PathTraceMaterial *uniform super, +void Glass_selectNextMedium(const uniform Material *uniform super, const DifferentialGeometry &dg, Medium ¤tMedium) { @@ -63,31 +56,17 @@ void Glass_selectNextMedium(const uniform PathTraceMaterial *uniform super, /////////////////////////////////////////////////////////////////////////////// // External API -export void PathTracer_Glass_set(void *uniform _self, - const uniform float iorInside, - const uniform vec3f &attenuationColorInside, - const uniform float outsideIor, - const uniform vec3f &attenuationColorOutside, - const uniform float attenuationDistance) +export void *uniform Glass_getBSDF_addr() { - uniform Glass *uniform self = (uniform Glass * uniform) _self; - - self->mediumInside.ior = iorInside; - self->mediumInside.attenuation = - logf(attenuationColorInside) / max(attenuationDistance, EPS); - self->mediumOutside.ior = outsideIor; - self->mediumOutside.attenuation = - logf(attenuationColorOutside) / max(attenuationDistance, EPS); + return Glass_getBSDF; } -export void *uniform PathTracer_Glass_create() +export void *uniform Glass_getTransparency_addr() { - uniform Glass *uniform self = uniform new uniform Glass; - PathTraceMaterial_Constructor(&self->super, - MATERIAL_TYPE_GLASS, - Glass_getBSDF, - Glass_getTransparency, - Glass_selectNextMedium); - - return self; + return Glass_getTransparency; } + +export void *uniform Glass_selectNextMedium_addr() +{ + return Glass_selectNextMedium; +} \ No newline at end of file diff --git a/modules/cpu/render/materials/GlassShared.h b/modules/cpu/render/materials/GlassShared.h new file mode 100644 index 0000000000..e86a742ccf --- /dev/null +++ b/modules/cpu/render/materials/GlassShared.h @@ -0,0 +1,23 @@ +// Copyright 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "MediumShared.h" +#include "render/MaterialShared.h" + +#ifdef __cplusplus +namespace ispc { +#endif // __cplusplus + +struct Glass +{ + Material super; + + Medium mediumInside; + Medium mediumOutside; +}; + +#ifdef __cplusplus +} // namespace ispc +#endif // __cplusplus diff --git a/modules/cpu/render/materials/Luminous.cpp b/modules/cpu/render/materials/Luminous.cpp index 6f1266bbe4..2d408a4ea6 100644 --- a/modules/cpu/render/materials/Luminous.cpp +++ b/modules/cpu/render/materials/Luminous.cpp @@ -10,7 +10,9 @@ namespace pathtracer { Luminous::Luminous() { - ispcEquivalent = ispc::PathTracer_Luminous_create(); + getSh()->super.type = ispc::MATERIAL_TYPE_LUMINOUS; + getSh()->super.getBSDF = ispc::Luminous_getBSDF_addr(); + getSh()->super.getTransparency = ispc::Luminous_getTransparency_addr(); } std::string Luminous::toString() const @@ -24,8 +26,8 @@ void Luminous::commit() getParam("color", vec3f(1.f)) * getParam("intensity", 1.f); const float transparency = getParam("transparency", 0.f); - ispc::PathTracer_Luminous_set( - getIE(), (const ispc::vec3f &)radiance, transparency); + getSh()->super.emission = radiance; + getSh()->transparency = transparency; } } // namespace pathtracer diff --git a/modules/cpu/render/materials/Luminous.h b/modules/cpu/render/materials/Luminous.h index 102130141f..06a3de638b 100644 --- a/modules/cpu/render/materials/Luminous.h +++ b/modules/cpu/render/materials/Luminous.h @@ -1,12 +1,14 @@ -// Copyright 2020 Intel Corporation +// Copyright 2020-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "render/Material.h" +#include "LuminousShared.h" + namespace ospray { namespace pathtracer { -struct Luminous : public ospray::Material +struct Luminous : public AddStructShared { Luminous(); diff --git a/modules/cpu/render/materials/Luminous.ispc b/modules/cpu/render/materials/Luminous.ispc index 8605435368..bd6a170b54 100644 --- a/modules/cpu/render/materials/Luminous.ispc +++ b/modules/cpu/render/materials/Luminous.ispc @@ -1,20 +1,16 @@ // Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 -#include "Material.ih" +#include "render/Material.ih" #include "render/bsdfs/Transmission.ih" -struct Luminous -{ - PathTraceMaterial super; - float transparency; -}; +#include "LuminousShared.h" /////////////////////////////////////////////////////////////////////////////// // Implementation const varying BSDF *varying Luminous_getBSDF( - const uniform PathTraceMaterial *uniform _self, + const uniform Material *uniform _self, uniform ShadingContext *uniform ctx, const DifferentialGeometry &dg, const Ray &, @@ -31,7 +27,7 @@ const varying BSDF *varying Luminous_getBSDF( return bsdf; } -vec3f Luminous_getTransparency(const uniform PathTraceMaterial *uniform _self, +vec3f Luminous_getTransparency(const uniform Material *uniform _self, const DifferentialGeometry &, const Ray &, const Medium &) @@ -41,31 +37,15 @@ vec3f Luminous_getTransparency(const uniform PathTraceMaterial *uniform _self, return make_vec3f(self->transparency); } -export void PathTracer_Luminous_set( - void *uniform, const uniform vec3f &, const uniform float); - -void Luminous_Constructor(uniform Luminous *uniform self) -{ - PathTraceMaterial_Constructor( - &self->super, MATERIAL_TYPE_LUMINOUS, Luminous_getBSDF, Luminous_getTransparency); - PathTracer_Luminous_set(self, make_vec3f(1.f), 0.f); -} - /////////////////////////////////////////////////////////////////////////////// // External API -export void PathTracer_Luminous_set(void *uniform _self, - const uniform vec3f &radiance, - const uniform float transparency) +export void *uniform Luminous_getBSDF_addr() { - uniform Luminous *uniform self = (uniform Luminous * uniform) _self; - self->super.emission = radiance; - self->transparency = transparency; + return Luminous_getBSDF; } -export void *uniform PathTracer_Luminous_create() +export void *uniform Luminous_getTransparency_addr() { - uniform Luminous *uniform self = uniform new uniform Luminous; - Luminous_Constructor(self); - return self; + return Luminous_getTransparency; } diff --git a/modules/cpu/render/materials/LuminousShared.h b/modules/cpu/render/materials/LuminousShared.h new file mode 100644 index 0000000000..f8e1cc4da2 --- /dev/null +++ b/modules/cpu/render/materials/LuminousShared.h @@ -0,0 +1,24 @@ +// Copyright 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "render/MaterialShared.h" + +#ifdef __cplusplus +namespace ispc { +#endif // __cplusplus + +struct Luminous +{ + Material super; + + float transparency; + +#ifdef __cplusplus + Luminous() : super(1.f), transparency(0.f) {} +}; +} // namespace ispc +#else +}; +#endif // __cplusplus \ No newline at end of file diff --git a/modules/cpu/render/materials/Material.ih b/modules/cpu/render/materials/Material.ih deleted file mode 100644 index 4280b1ab17..0000000000 --- a/modules/cpu/render/materials/Material.ih +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright 2009-2021 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include "Medium.ih" -#include "common/DifferentialGeometry.ih" -#include "common/Ray.ih" -#include "render/Material.ih" -#include "render/MaterialType.ih" -#include "render/bsdfs/BSDF.ih" -#include "render/bsdfs/ShadingContext.ih" -#include "texture/TextureParam.ih" - -#define EPS 1e-5f - -struct PathTraceMaterial; - -typedef const varying BSDF *varying (*PathTraceMaterial_GetBSDFFunc)( - const uniform PathTraceMaterial *uniform self, - uniform ShadingContext *uniform ctx, - /*! The point to shade on a surface. */ - const DifferentialGeometry &dg, - /*! The ray arriving at the point to shade. */ - const Ray &ray, - /*! The medium this ray travels inside. */ - const Medium ¤tMedium); - -// shortcut: compute transmission of material, for transparent shadows, -// neglecting refraction -typedef vec3f (*PathTraceMaterial_GetTransparencyFunc)( - const uniform PathTraceMaterial *uniform self, - /*! The point to shade on a surface. */ - const DifferentialGeometry &dg, - /*! The ray arriving at the point to shade. */ - const Ray &ray, - /*! The medium this ray travels inside. */ - const Medium ¤tMedium); - -typedef void (*PathTraceMaterial_SelectNextMediumFunc)( - const uniform PathTraceMaterial *uniform self, - const DifferentialGeometry &dg, - Medium ¤tMedium); - -struct PathTraceMaterial -{ - Material material; - PathTraceMaterial_GetBSDFFunc getBSDF; - PathTraceMaterial_GetTransparencyFunc getTransparency; - PathTraceMaterial_SelectNextMediumFunc selectNextMedium; - vec3f emission; // simple constant (spatially and angular) emission, returns - // radiance; TODO SV-EDFs -}; - -void PathTraceMaterial_Constructor(uniform PathTraceMaterial *uniform self, - uniform MaterialType material_type, - uniform PathTraceMaterial_GetBSDFFunc getBSDF, - uniform PathTraceMaterial_GetTransparencyFunc getTransparency = NULL, - uniform PathTraceMaterial_SelectNextMediumFunc selectNextMedium = NULL); -// const uniform vec3f emission = { 0.f, 0.f, 0.f }; fails, ISPC issue #1231 - -inline linear3f makeShadingFrame(const DifferentialGeometry &dg) -{ - vec3f N = dg.Ns; - vec3f U = normalize(dg.dPds); - vec3f V = cross(N, U); - if (dot(V, V) > 0.f) { - V = normalize(V); - U = cross(V, N); - return make_LinearSpace3f(U, V, N); - } else { - return frame(N); - } -} - -inline linear3f makeShadingFrame(const linear3f &f, const vec3f &localN) -{ - // in general f is not ortho-normal, thus need to re-normalize - vec3f N = normalize(f * localN); // transform normal to world space - vec3f U = f.vx; - vec3f V = cross(N, U); - if (dot(V, V) > 0.f) { - V = normalize(V); - U = cross(V, N); - return make_LinearSpace3f(U, V, N); - } else { - return frame(N); - } -} - -inline linear3f makeShadingFrame( - const DifferentialGeometry &dg, const vec3f &localN) -{ - linear3f f = makeShadingFrame(dg); - return makeShadingFrame(f, localN); -} - -inline linear3f makeShadingFrame(const DifferentialGeometry &dg, - const uniform TextureParam &normalMap, - const uniform linear2f &normalRot, - uniform float normalScale = 1.f) -{ - if (valid(normalMap)) { - linear3f f = - make_LinearSpace3f(normalize(dg.dPds), normalize(dg.dPdt), dg.Ns); - // get normal from texture - vec3f localN = - getNormal(normalMap, dg) * make_vec3f(normalScale, normalScale, 1.f); - // rotate in 2D (tangent space) to account for tc transformations - vec2f rotN = normalRot * make_vec2f(localN.x, localN.y); - localN.x = rotN.x; - localN.y = rotN.y; - return makeShadingFrame(f, localN); - } else { - return makeShadingFrame(dg); - } -} diff --git a/modules/cpu/render/materials/Material.ispc b/modules/cpu/render/materials/Material.ispc deleted file mode 100644 index 91b4201dc7..0000000000 --- a/modules/cpu/render/materials/Material.ispc +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2009-2021 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 - -#include "Material.ih" - -vec3f PathTraceMaterial_getTransparency( - const uniform PathTraceMaterial *uniform self, - const DifferentialGeometry &dg, - const Ray &ray, - const Medium ¤tMedium) -{ - return make_vec3f(0.0f); -} - -void PathTraceMaterial_selectNextMedium( - const uniform PathTraceMaterial *uniform self, - const DifferentialGeometry &dg, - Medium ¤tMedium) -{ /* do nothing by default */ -} - -void PathTraceMaterial_Constructor(uniform PathTraceMaterial *uniform self, - uniform MaterialType materialType, - uniform PathTraceMaterial_GetBSDFFunc getBSDF, - uniform PathTraceMaterial_GetTransparencyFunc getTransparency, - uniform PathTraceMaterial_SelectNextMediumFunc selectNextMedium) -{ - self->material.type = materialType; - - self->getBSDF = getBSDF; - self->getTransparency = - getTransparency ? getTransparency : PathTraceMaterial_getTransparency; - self->selectNextMedium = - selectNextMedium ? selectNextMedium : PathTraceMaterial_selectNextMedium; - self->emission = make_vec3f(0.f); -} - -export uniform bool PathTraceMaterial_isEmissive(void *uniform _mat) -{ - PathTraceMaterial *uniform mat = (PathTraceMaterial * uniform) _mat; - - return reduce_max(mat->emission) > 0.f; -} diff --git a/modules/cpu/render/materials/Medium.ih b/modules/cpu/render/materials/Medium.ih index 1a1f4c2893..82296bc27a 100644 --- a/modules/cpu/render/materials/Medium.ih +++ b/modules/cpu/render/materials/Medium.ih @@ -1,15 +1,11 @@ -// Copyright 2009-2020 Intel Corporation +// Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once + #include "rkcommon/math/vec.ih" -struct Medium -{ - vec3f attenuation; //!< negative Napierian attenuation coefficient, - // i.e. wrt. the natural base e - float ior; //!< Refraction index of medium. -}; +#include "MediumShared.h" inline Medium make_Medium(const vec3f attenuation, const float ior) { diff --git a/modules/cpu/render/materials/MediumShared.h b/modules/cpu/render/materials/MediumShared.h new file mode 100644 index 0000000000..1023d6fa7c --- /dev/null +++ b/modules/cpu/render/materials/MediumShared.h @@ -0,0 +1,22 @@ +// Copyright 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#ifdef __cplusplus +namespace ispc { +#endif // __cplusplus + +struct Medium +{ + vec3f attenuation; //!< negative Napierian attenuation coefficient, + // i.e. wrt. the natural base e + float ior; //!< Refraction index of medium. + +#ifdef __cplusplus + Medium() : attenuation(0.f), ior(1.f) {} +}; +} // namespace ispc +#else +}; +#endif // __cplusplus diff --git a/modules/cpu/render/materials/Metal.cpp b/modules/cpu/render/materials/Metal.cpp index 03e542d4ed..cd1db3b533 100644 --- a/modules/cpu/render/materials/Metal.cpp +++ b/modules/cpu/render/materials/Metal.cpp @@ -3,7 +3,6 @@ #include "Metal.h" #include "common/Data.h" -#include "math/spectrum.h" // ispc #include "render/materials/Metal_ispc.h" @@ -12,7 +11,8 @@ namespace pathtracer { Metal::Metal() { - ispcEquivalent = ispc::PathTracer_Metal_create(); + getSh()->super.type = ispc::MATERIAL_TYPE_METAL; + getSh()->super.getBSDF = ispc::Metal_getBSDF_addr(); } std::string Metal::toString() const @@ -23,12 +23,14 @@ std::string Metal::toString() const void Metal::commit() { auto ior = getParamDataT("ior"); - float etaResampled[SPECTRUM_SAMPLES]; - float kResampled[SPECTRUM_SAMPLES]; - float *etaSpectral = nullptr; - float *kSpectral = nullptr; + const vec3f &eta = getParam("eta", vec3f(RGB_AL_ETA)); + const vec3f &k = getParam("k", vec3f(RGB_AL_K)); + MaterialParam1f roughness = getMaterialParam1f("roughness", .1f); + if (ior) { // resample, relies on ordered samples + spectrum etaResampled; + spectrum kResampled; auto iorP = ior->begin(); auto iorPrev = *iorP; const auto iorLast = ior->end(); @@ -45,23 +47,18 @@ void Metal::commit() kResampled[l] = (1.f - f) * iorPrev.z + f * iorP->z; } } - etaSpectral = etaResampled; - kSpectral = kResampled; + getSh()->spectral = true; + getSh()->eta = etaResampled; + getSh()->k = kResampled; + } else { + // default to Aluminium, used when ior not given + getSh()->spectral = false; + getSh()->etaRGB = eta; + getSh()->kRGB = k; } - // default to Aluminium, used when ior not given - const vec3f &eta = getParam("eta", vec3f(RGB_AL_ETA)); - const vec3f &k = getParam("k", vec3f(RGB_AL_K)); - - MaterialParam1f roughness = getMaterialParam1f("roughness", .1f); - - ispc::PathTracer_Metal_set(getIE(), - etaSpectral, - kSpectral, - (const ispc::vec3f &)eta, - (const ispc::vec3f &)k, - roughness.factor, - roughness.tex); + getSh()->roughness = roughness.factor; + getSh()->roughnessMap = roughness.tex; } } // namespace pathtracer diff --git a/modules/cpu/render/materials/Metal.h b/modules/cpu/render/materials/Metal.h index 1982a03e09..38f7f00370 100644 --- a/modules/cpu/render/materials/Metal.h +++ b/modules/cpu/render/materials/Metal.h @@ -1,12 +1,15 @@ -// Copyright 2020 Intel Corporation +// Copyright 2020-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 +#include "math/spectrum.h" #include "render/Material.h" +#include "MetalShared.h" + namespace ospray { namespace pathtracer { -struct Metal : public ospray::Material +struct Metal : public AddStructShared { Metal(); diff --git a/modules/cpu/render/materials/Metal.ispc b/modules/cpu/render/materials/Metal.ispc index 492f18108c..e80a90d7c8 100644 --- a/modules/cpu/render/materials/Metal.ispc +++ b/modules/cpu/render/materials/Metal.ispc @@ -1,30 +1,18 @@ // Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 -#include "../bsdfs/Conductor.ih" -#include "../bsdfs/MicrofacetConductor.ih" -#include "Material.ih" #include "math/spectrum.ih" +#include "render/Material.ih" +#include "render/bsdfs/Conductor.ih" +#include "render/bsdfs/MicrofacetConductor.ih" #include "texture/TextureParam.ih" -struct Metal -{ - PathTraceMaterial super; - - bool spectral; - spectrum eta; // index of refraction - spectrum k; // index of refraction, imaginary part - vec3f etaRGB; // index of refraction - vec3f kRGB; // index of refraction, imaginary part - float roughness; // in [0, 1]; 0==ideally smooth (mirror) - TextureParam roughnessMap; -}; +#include "MetalShared.h" /////////////////////////////////////////////////////////////////////////////// // Implementation -const varying BSDF *varying Metal_getBSDF( - const uniform PathTraceMaterial *uniform super, +const varying BSDF *varying Metal_getBSDF(const uniform Material *uniform super, uniform ShadingContext *uniform ctx, const DifferentialGeometry &dg, const Ray &ray, @@ -50,41 +38,7 @@ const varying BSDF *varying Metal_getBSDF( /////////////////////////////////////////////////////////////////////////////// // External API -export void *uniform PathTracer_Metal_set(void *uniform _self, - const float *uniform eta, - const float *uniform k, - const uniform vec3f &etaRGB, - const uniform vec3f &kRGB, - uniform float roughness, - const uniform TextureParam &roughnessMap) -{ - Metal *uniform self = (Metal * uniform) _self; - - self->spectral = eta && k; - if (self->spectral) { - // actually could pass "uniform spectrum &", but may have different - // alignment on C++ side, thus copy element-wise - for (uniform int l = 0; l < SPECTRUM_SAMPLES; l++) { - self->eta[l] = eta[l]; - self->k[l] = k[l]; - } - } else { - self->etaRGB = etaRGB; - self->kRGB = kRGB; - } - - self->roughness = roughness; - self->roughnessMap = roughnessMap; -} - -export void *uniform PathTracer_Metal_create() +export void *uniform Metal_getBSDF_addr() { - Metal *uniform self = uniform new Metal; - PathTraceMaterial_Constructor(&self->super, MATERIAL_TYPE_METAL, Metal_getBSDF); - uniform vec3f etaRGB = RGB_AL_ETA; - uniform vec3f kRGB = RGB_AL_K; - - PathTracer_Metal_set( - self, NULL, NULL, etaRGB, kRGB, 0.1f, make_TextureParam()); - return self; + return Metal_getBSDF; } diff --git a/modules/cpu/render/materials/MetalShared.h b/modules/cpu/render/materials/MetalShared.h new file mode 100644 index 0000000000..011b08fb12 --- /dev/null +++ b/modules/cpu/render/materials/MetalShared.h @@ -0,0 +1,38 @@ +// Copyright 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "render/MaterialShared.h" +#include "texture/TextureParamShared.h" + +#ifdef __cplusplus +namespace ispc { +#endif // __cplusplus + +struct Metal +{ + Material super; + + bool spectral; + spectrum eta; // index of refraction + spectrum k; // index of refraction, imaginary part + vec3f etaRGB; // index of refraction + vec3f kRGB; // index of refraction, imaginary part + float roughness; // in [0, 1]; 0==ideally smooth (mirror) + TextureParam roughnessMap; + +#ifdef __cplusplus + Metal() + : spectral(false), + eta{}, + k{}, + etaRGB(RGB_AL_ETA), + kRGB(RGB_AL_K), + roughness(.1f) + {} +}; +} // namespace ispc +#else +}; +#endif // __cplusplus diff --git a/modules/cpu/render/materials/MetallicPaint.cpp b/modules/cpu/render/materials/MetallicPaint.cpp index 504d2091eb..e3be7759c8 100644 --- a/modules/cpu/render/materials/MetallicPaint.cpp +++ b/modules/cpu/render/materials/MetallicPaint.cpp @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 #include "MetallicPaint.h" -#include "math/spectrum.h" // ispc #include "render/materials/MetallicPaint_ispc.h" @@ -11,7 +10,8 @@ namespace pathtracer { MetallicPaint::MetallicPaint() { - ispcEquivalent = ispc::PathTracer_MetallicPaint_create(); + getSh()->super.type = ispc::MATERIAL_TYPE_METALLICPAINT; + getSh()->super.getBSDF = ispc::MetallicPaint_getBSDF_addr(); } std::string MetallicPaint::toString() const @@ -27,13 +27,12 @@ void MetallicPaint::commit() const float flakeSpread = getParam("flakeSpread", 0.5f); const float eta = getParam("eta", 1.5f); - ispc::PathTracer_MetallicPaint_set(getIE(), - (const ispc::vec3f &)color.factor, - color.tex, - flakeAmount, - (const ispc::vec3f &)flakeColor, - flakeSpread, - eta); + getSh()->baseColor = color.factor * (1.f - flakeAmount); + getSh()->baseColorMap = color.tex; + getSh()->flakeAmount = flakeAmount; + getSh()->flakeColor = flakeColor * flakeAmount; + getSh()->flakeSpread = flakeSpread; + getSh()->eta = rcp(eta); } } // namespace pathtracer diff --git a/modules/cpu/render/materials/MetallicPaint.h b/modules/cpu/render/materials/MetallicPaint.h index 7565a2a514..7b518e4704 100644 --- a/modules/cpu/render/materials/MetallicPaint.h +++ b/modules/cpu/render/materials/MetallicPaint.h @@ -1,12 +1,15 @@ -// Copyright 2009-2020 Intel Corporation +// Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 +#include "math/spectrum.h" #include "render/Material.h" +#include "MetallicPaintShared.h" + namespace ospray { namespace pathtracer { -struct MetallicPaint : public ospray::Material +struct MetallicPaint : public AddStructShared { MetallicPaint(); diff --git a/modules/cpu/render/materials/MetallicPaint.ispc b/modules/cpu/render/materials/MetallicPaint.ispc index e0ea4f3ed7..7ad974d65d 100644 --- a/modules/cpu/render/materials/MetallicPaint.ispc +++ b/modules/cpu/render/materials/MetallicPaint.ispc @@ -1,29 +1,20 @@ // Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 -#include "../bsdfs/DielectricLayer.ih" -#include "../bsdfs/Lambert.ih" -#include "../bsdfs/MicrofacetConductor.ih" -#include "../bsdfs/MultiBSDF.ih" -#include "Material.ih" +#include "render/Material.ih" +#include "render/bsdfs/DielectricLayer.ih" +#include "render/bsdfs/Lambert.ih" +#include "render/bsdfs/MicrofacetConductor.ih" +#include "render/bsdfs/MultiBSDF.ih" #include "texture/TextureParam.ih" -struct MetallicPaint -{ - PathTraceMaterial super; - vec3f baseColor; - TextureParam baseColorMap; - float flakeAmount; - vec3f flakeColor; - float flakeSpread; - float eta; -}; +#include "MetallicPaintShared.h" /////////////////////////////////////////////////////////////////////////////// // Implementation const varying BSDF *varying MetallicPaint_getBSDF( - const uniform PathTraceMaterial *uniform super, + const uniform Material *uniform super, uniform ShadingContext *uniform ctx, const DifferentialGeometry &dg, const Ray &ray, @@ -60,30 +51,7 @@ const varying BSDF *varying MetallicPaint_getBSDF( /////////////////////////////////////////////////////////////////////////////// // External API -export void *uniform PathTracer_MetallicPaint_set(void *uniform _self, - const uniform vec3f &baseColor, - const uniform TextureParam &baseColorMap, - const uniform float flakeAmount, - const uniform vec3f &flakeColor, - const uniform float flakeSpread, - const uniform float ior) +export void *uniform MetallicPaint_getBSDF_addr() { - MetallicPaint *uniform self = (MetallicPaint * uniform) _self; - self->flakeAmount = flakeAmount; - self->baseColor = baseColor * (1.f - flakeAmount); - self->baseColorMap = baseColorMap; - self->flakeColor = flakeColor * flakeAmount; - self->flakeSpread = flakeSpread; - self->eta = rcp(ior); -} - -export void *uniform PathTracer_MetallicPaint_create() -{ - uniform MetallicPaint *uniform self = uniform new uniform MetallicPaint; - PathTraceMaterial_Constructor(&self->super, MATERIAL_TYPE_METALLICPAINT, MetallicPaint_getBSDF); - const uniform vec3f flakeColor = RGB_AL_COLOR; - - PathTracer_MetallicPaint_set( - self, make_vec3f(0.5), make_TextureParam(), 0.5f, flakeColor, 0.5f, 1.6f); - return self; + return MetallicPaint_getBSDF; } diff --git a/modules/cpu/render/materials/MetallicPaintShared.h b/modules/cpu/render/materials/MetallicPaintShared.h new file mode 100644 index 0000000000..bf02416ecf --- /dev/null +++ b/modules/cpu/render/materials/MetallicPaintShared.h @@ -0,0 +1,36 @@ +// Copyright 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "render/MaterialShared.h" +#include "texture/TextureParamShared.h" + +#ifdef __cplusplus +namespace ispc { +#endif // __cplusplus + +struct MetallicPaint +{ + Material super; + + vec3f baseColor; + TextureParam baseColorMap; + float flakeAmount; + vec3f flakeColor; + float flakeSpread; + float eta; + +#ifdef __cplusplus + MetallicPaint() + : baseColor(.25f), + flakeAmount(.5f), + flakeColor(flakeAmount * vec3f(RGB_AL_COLOR)), + flakeSpread(.5), + eta(1.f / 1.6f) + {} +}; +} // namespace ispc +#else +}; +#endif // __cplusplus diff --git a/modules/cpu/render/materials/Mix.cpp b/modules/cpu/render/materials/Mix.cpp index c1bd2016f4..64245049e8 100644 --- a/modules/cpu/render/materials/Mix.cpp +++ b/modules/cpu/render/materials/Mix.cpp @@ -10,7 +10,9 @@ namespace pathtracer { MixMaterial::MixMaterial() { - ispcEquivalent = ispc::PathTracer_Mix_create(); + getSh()->super.type = ispc::MATERIAL_TYPE_MIX; + getSh()->super.getBSDF = ispc::Mix_getBSDF_addr(); + getSh()->super.getTransparency = ispc::Mix_getTransparency_addr(); } std::string MixMaterial::toString() const @@ -26,11 +28,17 @@ void MixMaterial::commit() ospray::Material *mat2 = (ospray::Material *)getParamObject("material2", nullptr); - ispc::PathTracer_Mix_set(ispcEquivalent, - factor.factor, - factor.tex, - mat1 ? mat1->getIE() : nullptr, - mat2 ? mat2->getIE() : nullptr); + getSh()->factor = clamp(factor.factor); + getSh()->factorMap = factor.tex; + getSh()->mat1 = mat1 ? mat1->getSh() : nullptr; + getSh()->mat2 = mat2 ? mat2->getSh() : nullptr; + + vec3f emission = vec3f(0.f); + if (mat1) + emission = (1.f - getSh()->factor) * getSh()->mat1->emission; + if (mat2) + emission = emission + getSh()->factor * getSh()->mat2->emission; + getSh()->super.emission = emission; } } // namespace pathtracer diff --git a/modules/cpu/render/materials/Mix.h b/modules/cpu/render/materials/Mix.h index 38ee933841..1fd30d9711 100644 --- a/modules/cpu/render/materials/Mix.h +++ b/modules/cpu/render/materials/Mix.h @@ -1,12 +1,14 @@ -// Copyright 2020 Intel Corporation +// Copyright 2020-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "render/Material.h" +#include "MixShared.h" + namespace ospray { namespace pathtracer { -struct MixMaterial : public ospray::Material +struct MixMaterial : public AddStructShared { MixMaterial(); diff --git a/modules/cpu/render/materials/Mix.ispc b/modules/cpu/render/materials/Mix.ispc index 5af39e2bf0..f867023e2f 100644 --- a/modules/cpu/render/materials/Mix.ispc +++ b/modules/cpu/render/materials/Mix.ispc @@ -1,25 +1,16 @@ // Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 -#include "Material.ih" +#include "render/Material.ih" #include "render/bsdfs/MultiBSDF.ih" #include "texture/TextureParam.ih" -struct Mix -{ - PathTraceMaterial super; - - float factor; - TextureParam factorMap; - PathTraceMaterial *mat1; - PathTraceMaterial *mat2; -}; +#include "MixShared.h" /////////////////////////////////////////////////////////////////////////////// // Implementation -const varying BSDF *varying Mix_getBSDF( - const uniform PathTraceMaterial *uniform super, +const varying BSDF *varying Mix_getBSDF(const uniform Material *uniform super, uniform ShadingContext *uniform ctx, const DifferentialGeometry &dg, const Ray &ray, @@ -45,7 +36,7 @@ const varying BSDF *varying Mix_getBSDF( return bsdf; } -vec3f Mix_getTransparency(const uniform PathTraceMaterial *uniform super, +vec3f Mix_getTransparency(const uniform Material *uniform super, const DifferentialGeometry &dg, const Ray &ray, const Medium ¤tMedium) @@ -67,35 +58,12 @@ vec3f Mix_getTransparency(const uniform PathTraceMaterial *uniform super, /////////////////////////////////////////////////////////////////////////////// // External API -export void PathTracer_Mix_set(void *uniform _self, - uniform float factor, - const uniform TextureParam &factorMap, - void *uniform _mat1, - void *uniform _mat2) -{ - Mix *uniform self = (Mix * uniform) _self; - self->factor = clamp(factor); - self->factorMap = factorMap; - self->mat1 = (PathTraceMaterial * uniform) _mat1; - self->mat2 = (PathTraceMaterial * uniform) _mat2; - - uniform vec3f emission = make_vec3f(0.f); - if (self->mat1) - emission = (1.f - self->factor) * self->mat1->emission; - if (self->mat2) - emission = emission + self->factor * self->mat2->emission; - self->super.emission = emission; -} - -void Mix_Constructor(uniform Mix *uniform self) +export void *uniform Mix_getBSDF_addr() { - PathTraceMaterial_Constructor(&self->super, MATERIAL_TYPE_MIX, Mix_getBSDF, Mix_getTransparency); - PathTracer_Mix_set(self, 0.5f, make_TextureParam(), NULL, NULL); + return Mix_getBSDF; } -export void *uniform PathTracer_Mix_create() +export void *uniform Mix_getTransparency_addr() { - Mix *uniform self = uniform new uniform Mix; - Mix_Constructor(self); - return self; + return Mix_getTransparency; } diff --git a/modules/cpu/render/materials/MixShared.h b/modules/cpu/render/materials/MixShared.h new file mode 100644 index 0000000000..5a6ac627fa --- /dev/null +++ b/modules/cpu/render/materials/MixShared.h @@ -0,0 +1,28 @@ +// Copyright 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "render/MaterialShared.h" +#include "texture/TextureParamShared.h" + +#ifdef __cplusplus +namespace ispc { +#endif // __cplusplus + +struct Mix +{ + Material super; + + float factor; + TextureParam factorMap; + Material *mat1; + Material *mat2; + +#ifdef __cplusplus + Mix() : factor(.5f), mat1(nullptr), mat2(nullptr) {} +}; +} // namespace ispc +#else +}; +#endif // __cplusplus \ No newline at end of file diff --git a/modules/cpu/render/materials/OBJ.cpp b/modules/cpu/render/materials/OBJ.cpp index 299186c96d..63c613523b 100644 --- a/modules/cpu/render/materials/OBJ.cpp +++ b/modules/cpu/render/materials/OBJ.cpp @@ -10,7 +10,9 @@ namespace pathtracer { OBJMaterial::OBJMaterial() { - ispcEquivalent = ispc::PathTracer_OBJ_create(); + getSh()->super.type = ispc::MATERIAL_TYPE_OBJ; + getSh()->super.getBSDF = ispc::OBJ_getBSDF_addr(); + getSh()->super.getTransparency = ispc::OBJ_getTransparency_addr(); } std::string OBJMaterial::toString() const @@ -40,18 +42,17 @@ void OBJMaterial::commit() Tf /= color_total; } - ispc::PathTracer_OBJ_set(ispcEquivalent, - d.tex, - d.factor, - Kd.tex, - (const ispc::vec3f &)Kd.factor, - Ks.tex, - (const ispc::vec3f &)Ks.factor, - Ns.tex, - Ns.factor, - (const ispc::vec3f &)Tf, - bumpTex, - (const ispc::LinearSpace2f &)bumpRot); + getSh()->dMap = d.tex; + getSh()->d = d.factor; + getSh()->KdMap = Kd.tex; + getSh()->Kd = Kd.factor; + getSh()->KsMap = Ks.tex; + getSh()->Ks = Ks.factor; + getSh()->NsMap = Ns.tex; + getSh()->Ns = Ns.factor; + getSh()->Tf = Tf; + getSh()->bumpMap = bumpTex; + getSh()->bumpRot = bumpRot; } } // namespace pathtracer diff --git a/modules/cpu/render/materials/OBJ.h b/modules/cpu/render/materials/OBJ.h index 5ee5be47b2..ca0511cf88 100644 --- a/modules/cpu/render/materials/OBJ.h +++ b/modules/cpu/render/materials/OBJ.h @@ -1,12 +1,14 @@ -// Copyright 2020 Intel Corporation +// Copyright 2020-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "render/Material.h" +#include "OBJShared.h" + namespace ospray { namespace pathtracer { -struct OBJMaterial : public ospray::Material +struct OBJMaterial : public AddStructShared { OBJMaterial(); diff --git a/modules/cpu/render/materials/OBJ.ih b/modules/cpu/render/materials/OBJ.ih index fbea318b1f..3d802155bf 100644 --- a/modules/cpu/render/materials/OBJ.ih +++ b/modules/cpu/render/materials/OBJ.ih @@ -2,22 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 #pragma once -#include "Material.ih" +#include "texture/TextureParam.ih" -struct OBJ -{ - PathTraceMaterial super; - - float d; // cut-out opacity - TextureParam dMap; - vec3f Kd; - TextureParam KdMap; - vec3f Ks; - TextureParam KsMap; - float Ns; - TextureParam NsMap; - vec3f Tf; // transmission filter - TextureParam bumpMap; - linear2f bumpRot; // just the inverse of rotational/mirror part (must be - // orthonormal) of tc xfrom -}; +#include "OBJShared.h" diff --git a/modules/cpu/render/materials/OBJ.ispc b/modules/cpu/render/materials/OBJ.ispc index 7d8ea9787e..629fef134e 100644 --- a/modules/cpu/render/materials/OBJ.ispc +++ b/modules/cpu/render/materials/OBJ.ispc @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 #include "OBJ.ih" +#include "render/Material.ih" #include "render/bsdfs/Lambert.ih" #include "render/bsdfs/MultiBSDF.ih" #include "render/bsdfs/Specular.ih" @@ -10,8 +11,7 @@ /////////////////////////////////////////////////////////////////////////////// // Implementation -const varying BSDF *varying OBJ_getBSDF( - const uniform PathTraceMaterial *uniform super, +const varying BSDF *varying OBJ_getBSDF(const uniform Material *uniform super, uniform ShadingContext *uniform ctx, const DifferentialGeometry &dg, const Ray &ray, @@ -56,7 +56,7 @@ const varying BSDF *varying OBJ_getBSDF( return bsdf; } -vec3f OBJ_getTransparency(const uniform PathTraceMaterial *uniform super, +vec3f OBJ_getTransparency(const uniform Material *uniform super, const DifferentialGeometry &dg, const Ray &ray, const Medium ¤tMedium) @@ -79,55 +79,12 @@ vec3f OBJ_getTransparency(const uniform PathTraceMaterial *uniform super, /////////////////////////////////////////////////////////////////////////////// // External API -export void PathTracer_OBJ_set(void *uniform _self, - const uniform TextureParam &dMap, - const uniform float d, - const uniform TextureParam &KdMap, - const uniform vec3f &Kd, - const uniform TextureParam &KsMap, - const uniform vec3f &Ks, - const uniform TextureParam &NsMap, - const uniform float Ns, - const uniform vec3f &Tf, - const uniform TextureParam &bumpMap, - const uniform linear2f &bumpRot) +export void *uniform OBJ_getBSDF_addr() { - uniform OBJ *uniform self = (uniform OBJ * uniform) _self; - self->dMap = dMap; - self->d = d; - self->KdMap = KdMap; - self->Kd = Kd; - self->KsMap = KsMap; - self->Ks = Ks; - self->NsMap = NsMap; - self->Ns = Ns; - self->Tf = Tf; - self->bumpMap = bumpMap; - self->bumpRot = bumpRot; + return OBJ_getBSDF; } -void OBJ_Constructor(uniform OBJ *uniform self) +export void *uniform OBJ_getTransparency_addr() { - PathTraceMaterial_Constructor(&self->super, MATERIAL_TYPE_OBJ, OBJ_getBSDF, OBJ_getTransparency); - - uniform TextureParam tex = make_TextureParam(); - PathTracer_OBJ_set(self, - tex, - 1.0f, - tex, - make_vec3f(0.8f), - tex, - make_vec3f(0.f), - tex, - 2.0f, - make_vec3f(0.f), - tex, - make_LinearSpace2f_identity()); -} - -export void *uniform PathTracer_OBJ_create() -{ - uniform OBJ *uniform self = uniform new uniform OBJ; - OBJ_Constructor(self); - return self; + return OBJ_getTransparency; } diff --git a/modules/cpu/render/materials/OBJShared.h b/modules/cpu/render/materials/OBJShared.h new file mode 100644 index 0000000000..509ec64cd6 --- /dev/null +++ b/modules/cpu/render/materials/OBJShared.h @@ -0,0 +1,36 @@ +// Copyright 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "render/MaterialShared.h" +#include "texture/TextureParamShared.h" + +#ifdef __cplusplus +namespace ispc { +#endif // __cplusplus + +struct OBJ +{ + Material super; + + float d; // cut-out opacity + TextureParam dMap; + vec3f Kd; + TextureParam KdMap; + vec3f Ks; + TextureParam KsMap; + float Ns; + TextureParam NsMap; + vec3f Tf; // transmission filter + TextureParam bumpMap; + linear2f bumpRot; // just the inverse of rotational/mirror part (must be + // orthonormal) of tc xfrom + +#ifdef __cplusplus + OBJ() : d(1.f), Kd(.8f), Ks(0.f), Ns(2.f), Tf(0.f), bumpRot(one) {} +}; +} // namespace ispc +#else +}; +#endif // __cplusplus diff --git a/modules/cpu/render/materials/Plastic.cpp b/modules/cpu/render/materials/Plastic.cpp index fe4f59d1be..b88df90b7a 100644 --- a/modules/cpu/render/materials/Plastic.cpp +++ b/modules/cpu/render/materials/Plastic.cpp @@ -10,7 +10,8 @@ namespace pathtracer { Plastic::Plastic() { - ispcEquivalent = ispc::PathTracer_Plastic_create(); + getSh()->super.type = ispc::MATERIAL_TYPE_PLASTIC; + getSh()->super.getBSDF = ispc::Plastic_getBSDF_addr(); } std::string Plastic::toString() const @@ -24,8 +25,9 @@ void Plastic::commit() const float eta = getParam("eta", 1.4f); const float roughness = getParam("roughness", 0.01f); - ispc::PathTracer_Plastic_set( - getIE(), (const ispc::vec3f &)pigmentColor, eta, roughness); + getSh()->pigmentColor = pigmentColor; + getSh()->eta = rcp(eta); + getSh()->roughness = roughness; } } // namespace pathtracer diff --git a/modules/cpu/render/materials/Plastic.h b/modules/cpu/render/materials/Plastic.h index 17e0c32194..58c418ffac 100644 --- a/modules/cpu/render/materials/Plastic.h +++ b/modules/cpu/render/materials/Plastic.h @@ -1,12 +1,14 @@ -// Copyright 2020 Intel Corporation +// Copyright 2020-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "render/Material.h" +#include "PlasticShared.h" + namespace ospray { namespace pathtracer { -struct Plastic : public ospray::Material +struct Plastic : public AddStructShared { Plastic(); diff --git a/modules/cpu/render/materials/Plastic.ispc b/modules/cpu/render/materials/Plastic.ispc index 47e79a92c6..98af3aad25 100644 --- a/modules/cpu/render/materials/Plastic.ispc +++ b/modules/cpu/render/materials/Plastic.ispc @@ -1,24 +1,18 @@ // Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 -#include "../bsdfs/DielectricLayer.ih" -#include "../bsdfs/Lambert.ih" -#include "../bsdfs/MicrofacetDielectricLayer.ih" -#include "Material.ih" +#include "render/Material.ih" +#include "render/bsdfs/DielectricLayer.ih" +#include "render/bsdfs/Lambert.ih" +#include "render/bsdfs/MicrofacetDielectricLayer.ih" -struct Plastic -{ - PathTraceMaterial super; - vec3f pigmentColor; - float eta; - float roughness; -}; +#include "PlasticShared.h" /////////////////////////////////////////////////////////////////////////////// // Implementation const varying BSDF *varying Plastic_getBSDF( - const uniform PathTraceMaterial *uniform super, + const uniform Material *uniform super, uniform ShadingContext *uniform ctx, const DifferentialGeometry &dg, const Ray &ray, @@ -49,32 +43,10 @@ const varying BSDF *varying Plastic_getBSDF( return bsdf; } -void Plastic_Constructor(uniform Plastic *uniform self, - const uniform vec3f &pigmentColor, - uniform float ior, - uniform float roughness) -{ - PathTraceMaterial_Constructor(&self->super, MATERIAL_TYPE_PLASTIC, Plastic_getBSDF); - self->pigmentColor = pigmentColor; - self->eta = rcp(ior); - self->roughness = roughness; -} - /////////////////////////////////////////////////////////////////////////////// // External API -export void *uniform PathTracer_Plastic_create() -{ - uniform Plastic *uniform self = uniform new uniform Plastic; - return self; -} - -export void *uniform PathTracer_Plastic_set(void *uniform _self, - const uniform vec3f &pigmentColor, - uniform float ior, - uniform float roughness) +export void *uniform Plastic_getBSDF_addr() { - uniform Plastic *uniform self = (uniform Plastic * uniform) _self; - Plastic_Constructor(self, pigmentColor, ior, roughness); - return self; + return Plastic_getBSDF; } diff --git a/modules/cpu/render/materials/PlasticShared.h b/modules/cpu/render/materials/PlasticShared.h new file mode 100644 index 0000000000..f45df2e79f --- /dev/null +++ b/modules/cpu/render/materials/PlasticShared.h @@ -0,0 +1,26 @@ +// Copyright 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "render/MaterialShared.h" + +#ifdef __cplusplus +namespace ispc { +#endif // __cplusplus + +struct Plastic +{ + Material super; + + vec3f pigmentColor; + float eta; + float roughness; + +#ifdef __cplusplus + Plastic() : pigmentColor(1.f), eta(1.4f), roughness(0.01f) {} +}; +} // namespace ispc +#else +}; +#endif // __cplusplus diff --git a/modules/cpu/render/materials/Principled.cpp b/modules/cpu/render/materials/Principled.cpp index 1eb654f2a6..40a88e9220 100644 --- a/modules/cpu/render/materials/Principled.cpp +++ b/modules/cpu/render/materials/Principled.cpp @@ -2,8 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 #include "Principled.h" -#include "common/Data.h" -#include "math/spectrum.h" // ispc #include "render/materials/Principled_ispc.h" @@ -12,7 +10,10 @@ namespace pathtracer { Principled::Principled() { - ispcEquivalent = ispc::PathTracer_Principled_create(); + getSh()->super.type = ispc::MATERIAL_TYPE_PRINCIPLED; + getSh()->super.getBSDF = ispc::Principled_getBSDF_addr(); + getSh()->super.getTransparency = ispc::Principled_getTransparency_addr(); + getSh()->super.selectNextMedium = ispc::Principled_selectNextMedium_addr(); } std::string Principled::toString() const @@ -63,73 +64,69 @@ void Principled::commit() float outsideTransmissionDepth = getParam("outsideTransmissionDepth", 1.f); - ispc::PathTracer_Principled_set(getIE(), - (const ispc::vec3f &)baseColor.factor, - baseColor.tex, - (const ispc::vec3f &)edgeColor.factor, - edgeColor.tex, - metallic.factor, - metallic.tex, - diffuse.factor, - diffuse.tex, - specular.factor, - specular.tex, - ior.factor, - ior.tex, - transmission.factor, - transmission.tex, - (const ispc::vec3f &)transmissionColor.factor, - transmissionColor.tex, - transmissionDepth.factor, - transmissionDepth.tex, - roughness.factor, - roughness.tex, - anisotropy.factor, - anisotropy.tex, - rotation.factor, - rotation.tex, - normal.factor, - normal.tex, - (const ispc::LinearSpace2f &)normal.rot, - baseNormal.factor, - baseNormal.tex, - (const ispc::LinearSpace2f &)baseNormal.rot, - - coat.factor, - coat.tex, - coatIor.factor, - coatIor.tex, - (const ispc::vec3f &)coatColor.factor, - coatColor.tex, - coatThickness.factor, - coatThickness.tex, - coatRoughness.factor, - coatRoughness.tex, - coatNormal.factor, - coatNormal.tex, - (const ispc::LinearSpace2f &)coatNormal.rot, - - sheen.factor, - sheen.tex, - (const ispc::vec3f &)sheenColor.factor, - sheenColor.tex, - sheenTint.factor, - sheenTint.tex, - sheenRoughness.factor, - sheenRoughness.tex, - - opacity.factor, - opacity.tex, - - thin, - backlight.factor, - backlight.tex, - thickness.factor, - thickness.tex, - - outsideIor, - (const ispc::vec3f &)outsideTransmissionColor, - outsideTransmissionDepth); + getSh()->baseColor = baseColor.factor; + getSh()->baseColorMap = baseColor.tex; + getSh()->edgeColor = edgeColor.factor; + getSh()->edgeColorMap = edgeColor.tex; + getSh()->metallic = metallic.factor; + getSh()->metallicMap = metallic.tex; + getSh()->diffuse = diffuse.factor; + getSh()->diffuseMap = diffuse.tex; + getSh()->specular = specular.factor; + getSh()->specularMap = specular.tex; + getSh()->ior = ior.factor; + getSh()->iorMap = ior.tex; + getSh()->transmission = transmission.factor; + getSh()->transmissionMap = transmission.tex; + getSh()->transmissionColor = transmissionColor.factor; + getSh()->transmissionColorMap = transmissionColor.tex; + getSh()->transmissionDepth = transmissionDepth.factor; + getSh()->transmissionDepthMap = transmissionDepth.tex; + getSh()->roughness = roughness.factor; + getSh()->roughnessMap = roughness.tex; + getSh()->anisotropy = anisotropy.factor; + getSh()->anisotropyMap = anisotropy.tex; + getSh()->rotation = rotation.factor; + getSh()->rotationMap = rotation.tex; + getSh()->normal = normal.factor; + getSh()->normalMap = normal.tex; + getSh()->normalRot = normal.rot; + getSh()->baseNormal = baseNormal.factor; + getSh()->baseNormalMap = baseNormal.tex; + getSh()->baseNormalRot = baseNormal.rot; + getSh()->coat = coat.factor; + getSh()->coatMap = coat.tex; + getSh()->coatIor = coatIor.factor; + getSh()->coatIorMap = coatIor.tex; + getSh()->coatColor = coatColor.factor; + getSh()->coatColorMap = coatColor.tex; + getSh()->coatThickness = coatThickness.factor; + getSh()->coatThicknessMap = coatThickness.tex; + getSh()->coatRoughness = coatRoughness.factor; + getSh()->coatRoughnessMap = coatRoughness.tex; + getSh()->coatNormal = coatNormal.factor; + getSh()->coatNormalMap = coatNormal.tex; + getSh()->coatNormalRot = coatNormal.rot; + getSh()->sheen = sheen.factor; + getSh()->sheenMap = sheen.tex; + getSh()->sheenColor = sheenColor.factor; + getSh()->sheenColorMap = sheenColor.tex; + getSh()->sheenTint = sheenTint.factor; + getSh()->sheenTintMap = sheenTint.tex; + getSh()->sheenRoughness = sheenRoughness.factor; + getSh()->sheenRoughnessMap = sheenRoughness.tex; + getSh()->opacity = opacity.factor; + getSh()->opacityMap = opacity.tex; + getSh()->thin = thin; + getSh()->backlight = backlight.factor; + getSh()->backlightMap = backlight.tex; + getSh()->thickness = thickness.factor; + getSh()->thicknessMap = thickness.tex; + getSh()->outsideMedium.ior = outsideIor >= 1.f ? outsideIor : rcp(outsideIor); + vec3f otc = vec3f(log(outsideTransmissionColor.x), + log(outsideTransmissionColor.y), + log(outsideTransmissionColor.z)); + getSh()->outsideMedium.attenuation = otc / outsideTransmissionDepth; } } // namespace pathtracer diff --git a/modules/cpu/render/materials/Principled.h b/modules/cpu/render/materials/Principled.h index 750f244e9f..b7b760c19d 100644 --- a/modules/cpu/render/materials/Principled.h +++ b/modules/cpu/render/materials/Principled.h @@ -1,12 +1,14 @@ -// Copyright 2020 Intel Corporation +// Copyright 2020-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "render/Material.h" +#include "PrincipledShared.h" + namespace ospray { namespace pathtracer { -struct Principled : public ospray::Material +struct Principled : public AddStructShared { Principled(); diff --git a/modules/cpu/render/materials/Principled.ispc b/modules/cpu/render/materials/Principled.ispc index ff6c59661d..40bf97c6f3 100644 --- a/modules/cpu/render/materials/Principled.ispc +++ b/modules/cpu/render/materials/Principled.ispc @@ -1,141 +1,31 @@ // Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 -#include "../bsdfs/Conductor.ih" -#include "../bsdfs/Dielectric.ih" -#include "../bsdfs/DielectricLayer.ih" -#include "../bsdfs/Lambert.ih" -#include "../bsdfs/LambertTransmission.ih" -#include "../bsdfs/MicrofacetConductor.ih" -#include "../bsdfs/MicrofacetDielectric.ih" -#include "../bsdfs/MicrofacetDielectricLayer.ih" -#include "../bsdfs/MicrofacetSheenLayer.ih" -#include "../bsdfs/OrenNayar.ih" -#include "../bsdfs/ThinDielectric.ih" -#include "../bsdfs/ThinMicrofacetDielectric.ih" -#include "../bsdfs/Transmission.ih" -#include "Material.ih" -#include "math/spectrum.ih" +#include "Medium.ih" +#include "render/Material.ih" +#include "render/bsdfs/Conductor.ih" +#include "render/bsdfs/Dielectric.ih" +#include "render/bsdfs/DielectricLayer.ih" +#include "render/bsdfs/Lambert.ih" +#include "render/bsdfs/LambertTransmission.ih" +#include "render/bsdfs/MicrofacetConductor.ih" +#include "render/bsdfs/MicrofacetDielectric.ih" +#include "render/bsdfs/MicrofacetDielectricLayer.ih" +#include "render/bsdfs/MicrofacetSheenLayer.ih" #include "render/bsdfs/MultiBSDF.ih" +#include "render/bsdfs/OrenNayar.ih" +#include "render/bsdfs/ThinDielectric.ih" +#include "render/bsdfs/ThinMicrofacetDielectric.ih" +#include "render/bsdfs/Transmission.ih" #include "texture/TextureParam.ih" -struct Principled -{ - PathTraceMaterial super; - - vec3f baseColor; - TextureParam baseColorMap; - - // metallic reflectivity at grazing angle (90 deg) / edge tint - vec3f edgeColor; - TextureParam edgeColorMap; - - float metallic; - TextureParam metallicMap; - - // diffuse weight in [0, 1] - float diffuse; - TextureParam diffuseMap; - - // specular weight in [0, 1] - float specular; - TextureParam specularMap; - - // index of refraction - float ior; - TextureParam iorMap; - - // specular transmission in [0, 1] - float transmission; - TextureParam transmissionMap; - - vec3f transmissionColor; - TextureParam transmissionColorMap; - - float transmissionDepth; - TextureParam transmissionDepthMap; - - // roughness in [0, 1]; 0 = ideally smooth (mirror) - float roughness; - TextureParam roughnessMap; - - // degree of anisotropy in [0, 1]; 0 = isotropic, 1 = maximally anisotropic - float anisotropy; - TextureParam anisotropyMap; - - // anisotropic rotation in [0, 1]; - float rotation; - TextureParam rotationMap; - - // default normal map for all layers - float normal; // scale - TextureParam normalMap; - linear2f normalRot; - - // base normal map (overrides default normal) - float baseNormal; // scale - TextureParam baseNormalMap; - linear2f baseNormalRot; - - // dielectric clear coat weight in [0, 1] - float coat; - TextureParam coatMap; - - // dielectric clear coat index of refraction - float coatIor; - TextureParam coatIorMap; - - vec3f coatColor; - TextureParam coatColorMap; - - float coatThickness; - TextureParam coatThicknessMap; - - float coatRoughness; - TextureParam coatRoughnessMap; - - // clear coat normal map (overrides default normal) - float coatNormal; // scale - TextureParam coatNormalMap; - linear2f coatNormalRot; - - // sheen weight in [0, 1] - float sheen; - TextureParam sheenMap; - - vec3f sheenColor; - TextureParam sheenColorMap; - - // sheen tint in [0, 1] - float sheenTint; - TextureParam sheenTintMap; - - float sheenRoughness; - TextureParam sheenRoughnessMap; - - // cut-out opacity in [0, 1] - float opacity; - TextureParam opacityMap; - - // solid or thin mode flag - bool thin; - - // diffuse transmission in [0, 2] (thin only) - float backlight; - TextureParam backlightMap; - - // thickness (thin only) - float thickness; - TextureParam thicknessMap; - - Medium outsideMedium; -}; +#include "PrincipledShared.h" /////////////////////////////////////////////////////////////////////////////// // Implementation const varying BSDF *varying Principled_getBSDF( - const uniform PathTraceMaterial *uniform super, + const uniform Material *uniform super, uniform ShadingContext *uniform ctx, const DifferentialGeometry &dg, const Ray &ray, @@ -213,9 +103,8 @@ const varying BSDF *varying Principled_getBSDF( clamp(self->diffuse * get1f(self->diffuseMap, dg, 1.f)); const vec3f diffuseColor = baseColor * diffuse; const float backlight = self->thin - ? clamp(self->backlight * get1f(self->backlightMap, dg, 1.f), - 0.f, - 2.f) + ? clamp( + self->backlight * get1f(self->backlightMap, dg, 1.f), 0.f, 2.f) : 0.f; const float diffuseTransmission = backlight * 0.5f; const float diffuseReflection = 1.f - diffuseTransmission; @@ -417,8 +306,7 @@ const varying BSDF *varying Principled_getBSDF( return bsdf; } -vec3f Principled_getTransparency( - const uniform PathTraceMaterial *uniform material, +vec3f Principled_getTransparency(const uniform Material *uniform material, const DifferentialGeometry &dg, const Ray &ray, const Medium ¤tMedium) @@ -521,16 +409,16 @@ vec3f Principled_getTransparency( if (coatRoughness < EPS) { T = T * DielectricLayer_getTransparency( - cosThetaO, coatEta, coatColor, coatThickness, coat); + cosThetaO, coatEta, coatColor, coatThickness, coat); } else { T = T * MicrofacetDielectricLayer_getTransparency(cosThetaO, - coatEta, - coatColor, - coatThickness, - coatRoughness, - 0.f, - coat); + coatEta, + coatColor, + coatThickness, + coatRoughness, + 0.f, + coat); } } } @@ -543,7 +431,7 @@ vec3f Principled_getTransparency( T = T * MicrofacetSheenLayer_getTransparency( - cosThetaO, sheenRoughness, sheen); + cosThetaO, sheenRoughness, sheen); } T = T * glass; @@ -557,7 +445,7 @@ vec3f Principled_getTransparency( return T; } -void Principled_selectNextMedium(const uniform PathTraceMaterial *uniform super, +void Principled_selectNextMedium(const uniform Material *uniform super, const DifferentialGeometry &dg, Medium ¤tMedium) { @@ -587,236 +475,17 @@ void Principled_selectNextMedium(const uniform PathTraceMaterial *uniform super, /////////////////////////////////////////////////////////////////////////////// // External API -export void *uniform PathTracer_Principled_set(void *uniform _self, - const uniform vec3f &baseColor, - const uniform TextureParam &baseColorMap, - const uniform vec3f &edgeColor, - const uniform TextureParam &edgeColorMap, - uniform float metallic, - const uniform TextureParam &metallicMap, - uniform float diffuse, - const uniform TextureParam &diffuseMap, - uniform float specular, - const uniform TextureParam &specularMap, - uniform float ior, - const uniform TextureParam &iorMap, - uniform float transmission, - const uniform TextureParam &transmissionMap, - const uniform vec3f &transmissionColor, - const uniform TextureParam &transmissionColorMap, - uniform float transmissionDepth, - const uniform TextureParam &transmissionDepthMap, - uniform float roughness, - const uniform TextureParam &roughnessMap, - uniform float anisotropy, - const uniform TextureParam &anisotropyMap, - uniform float rotation, - const uniform TextureParam &rotationMap, - uniform float normal, - const uniform TextureParam &normalMap, - const uniform linear2f &normalRot, - uniform float baseNormal, - const uniform TextureParam &baseNormalMap, - const uniform linear2f &baseNormalRot, - uniform float coat, - const uniform TextureParam &coatMap, - uniform float coatIor, - const uniform TextureParam &coatIorMap, - const uniform vec3f &coatColor, - const uniform TextureParam &coatColorMap, - uniform float coatThickness, - const uniform TextureParam &coatThicknessMap, - uniform float coatRoughness, - const uniform TextureParam &coatRoughnessMap, - uniform float coatNormal, - const uniform TextureParam &coatNormalMap, - const uniform linear2f &coatNormalRot, - uniform float sheen, - const uniform TextureParam &sheenMap, - const uniform vec3f &sheenColor, - const uniform TextureParam &sheenColorMap, - uniform float sheenTint, - const uniform TextureParam &sheenTintMap, - uniform float sheenRoughness, - const uniform TextureParam &sheenRoughnessMap, - uniform float opacity, - const uniform TextureParam &opacityMap, - uniform bool thin, - uniform float backlight, - const uniform TextureParam &backlightMap, - uniform float thickness, - const uniform TextureParam &thicknessMap, - uniform float outsideIor, - const uniform vec3f &outsideTransmissionColor, - uniform float outsideTransmissionDepth) +export void *uniform Principled_getBSDF_addr() { - Principled *uniform self = (Principled * uniform) _self; - - self->baseColor = baseColor; - self->baseColorMap = baseColorMap; - - self->edgeColor = edgeColor; - self->edgeColorMap = edgeColorMap; - - self->metallic = metallic; - self->metallicMap = metallicMap; - - self->diffuse = diffuse; - self->diffuseMap = diffuseMap; - - self->specular = specular; - self->specularMap = specularMap; - - self->ior = ior; - self->iorMap = iorMap; - - self->transmission = transmission; - self->transmissionMap = transmissionMap; - - self->transmissionColor = transmissionColor; - self->transmissionColorMap = transmissionColorMap; - - self->transmissionDepth = transmissionDepth; - self->transmissionDepthMap = transmissionDepthMap; - - self->roughness = roughness; - self->roughnessMap = roughnessMap; - - self->anisotropy = anisotropy; - self->anisotropyMap = anisotropyMap; - - self->rotation = rotation; - self->rotationMap = rotationMap; - - self->normal = normal; - self->normalMap = normalMap; - self->normalRot = normalRot; - - self->baseNormal = baseNormal; - self->baseNormalMap = baseNormalMap; - self->baseNormalRot = baseNormalRot; - - self->coat = coat; - self->coatMap = coatMap; - - self->coatIor = coatIor; - self->coatIorMap = coatIorMap; - - self->coatColor = coatColor; - self->coatColorMap = coatColorMap; - - self->coatThickness = coatThickness; - self->coatThicknessMap = coatThicknessMap; - - self->coatRoughness = coatRoughness; - self->coatRoughnessMap = coatRoughnessMap; - - self->coatNormal = coatNormal; - self->coatNormalMap = coatNormalMap; - self->coatNormalRot = coatNormalRot; - - self->sheen = sheen; - self->sheenMap = sheenMap; - - self->sheenColor = sheenColor; - self->sheenColorMap = sheenColorMap; - - self->sheenTint = sheenTint; - self->sheenTintMap = sheenTintMap; - - self->sheenRoughness = sheenRoughness; - self->sheenRoughnessMap = sheenRoughnessMap; - - self->opacity = opacity; - self->opacityMap = opacityMap; - - self->thin = thin; - - self->backlight = backlight; - self->backlightMap = backlightMap; - - self->thickness = thickness; - self->thicknessMap = thicknessMap; + return Principled_getBSDF; +} - self->outsideMedium.ior = outsideIor >= 1.f ? outsideIor : rcp(outsideIor); - self->outsideMedium.attenuation = - logf(outsideTransmissionColor) / outsideTransmissionDepth; +export void *uniform Principled_getTransparency_addr() +{ + return Principled_getTransparency; } -export void *uniform PathTracer_Principled_create() +export void *uniform Principled_selectNextMedium_addr() { - Principled *uniform self = uniform new Principled; - - PathTraceMaterial_Constructor(&self->super, - MATERIAL_TYPE_PRINCIPLED, - Principled_getBSDF, - Principled_getTransparency, - Principled_selectNextMedium); - - uniform TextureParam tex = make_TextureParam(); - PathTracer_Principled_set(self, - make_vec3f(0.8f), - tex, // baseColor - make_vec3f(1.f), - tex, // edgeColor - 0.f, - tex, // metallic - 1.f, - tex, // diffuse - 1.f, - tex, // specular - 1.f, - tex, // ior - 0.f, - tex, // transmission - make_vec3f(1.f), - tex, // transmissionColor - 1.f, - tex, // transmissionDepth - 0.f, - tex, // roughness - 0.f, - tex, // anisotropy - 0.f, - tex, // rotation - 1.f, - tex, - make_LinearSpace2f_identity(), // normal - 1.f, - tex, - make_LinearSpace2f_identity(), // baseNormal - 0.f, - tex, // coat - 1.5f, - tex, // coatIor - make_vec3f(1.f), - tex, // coatColor - 1.f, - tex, // coatThickness - 0.f, - tex, // coatRoughness - 1.f, - tex, - make_LinearSpace2f_identity(), // coatNormal - 0.f, - tex, // sheen - make_vec3f(1.f), - tex, // sheenColor - 0.f, - tex, // sheenTint - 0.2f, - tex, // sheenRoughness - 1.f, - tex, // opacity - false, // thin - 0.f, - tex, // backlight - 1.f, - tex, // thickness - 1.f, // outsideIor - make_vec3f(1.f), // outsideTransmissionColor - 1.f // outsideTransmissionDepth - ); - - return self; + return Principled_selectNextMedium; } diff --git a/modules/cpu/render/materials/PrincipledShared.h b/modules/cpu/render/materials/PrincipledShared.h new file mode 100644 index 0000000000..969aa17f37 --- /dev/null +++ b/modules/cpu/render/materials/PrincipledShared.h @@ -0,0 +1,163 @@ +// Copyright 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "MediumShared.h" +#include "render/MaterialShared.h" +#include "texture/TextureParamShared.h" + +#ifdef __cplusplus +namespace ispc { +#endif // __cplusplus + +struct Principled +{ + Material super; + + vec3f baseColor; + TextureParam baseColorMap; + + // metallic reflectivity at grazing angle (90 deg) / edge tint + vec3f edgeColor; + TextureParam edgeColorMap; + + float metallic; + TextureParam metallicMap; + + // diffuse weight in [0, 1] + float diffuse; + TextureParam diffuseMap; + + // specular weight in [0, 1] + float specular; + TextureParam specularMap; + + // index of refraction + float ior; + TextureParam iorMap; + + // specular transmission in [0, 1] + float transmission; + TextureParam transmissionMap; + + vec3f transmissionColor; + TextureParam transmissionColorMap; + + float transmissionDepth; + TextureParam transmissionDepthMap; + + // roughness in [0, 1]; 0 = ideally smooth (mirror) + float roughness; + TextureParam roughnessMap; + + // degree of anisotropy in [0, 1]; 0 = isotropic, 1 = maximally anisotropic + float anisotropy; + TextureParam anisotropyMap; + + // anisotropic rotation in [0, 1]; + float rotation; + TextureParam rotationMap; + + // default normal map for all layers + float normal; // scale + TextureParam normalMap; + linear2f normalRot; + + // base normal map (overrides default normal) + float baseNormal; // scale + TextureParam baseNormalMap; + linear2f baseNormalRot; + + // dielectric clear coat weight in [0, 1] + float coat; + TextureParam coatMap; + + // dielectric clear coat index of refraction + float coatIor; + TextureParam coatIorMap; + + vec3f coatColor; + TextureParam coatColorMap; + + float coatThickness; + TextureParam coatThicknessMap; + + float coatRoughness; + TextureParam coatRoughnessMap; + + // clear coat normal map (overrides default normal) + float coatNormal; // scale + TextureParam coatNormalMap; + linear2f coatNormalRot; + + // sheen weight in [0, 1] + float sheen; + TextureParam sheenMap; + + vec3f sheenColor; + TextureParam sheenColorMap; + + // sheen tint in [0, 1] + float sheenTint; + TextureParam sheenTintMap; + + float sheenRoughness; + TextureParam sheenRoughnessMap; + + // cut-out opacity in [0, 1] + float opacity; + TextureParam opacityMap; + + // solid or thin mode flag + bool thin; + + // diffuse transmission in [0, 2] (thin only) + float backlight; + TextureParam backlightMap; + + // thickness (thin only) + float thickness; + TextureParam thicknessMap; + + Medium outsideMedium; + +#ifdef __cplusplus + Principled() + : baseColor(0.8f), + edgeColor(1.f), + metallic(0.f), + diffuse(1.f), + specular(1.f), + ior(1.f), + transmission(0.f), + transmissionColor(1.f), + transmissionDepth(1.f), + roughness(0.f), + anisotropy(0.f), + rotation(0.f), + normal(1.f), + normalRot(one), + baseNormal(1.f), + baseNormalRot(one), + coat(0.f), + coatIor(1.5f), + coatColor(1.f), + coatThickness(1.f), + coatRoughness(0.f), + coatNormal(1.f), + coatNormalRot(one), + sheen(0.f), + sheenColor(1.f), + sheenTint(0.f), + sheenRoughness(0.2f), + opacity(1.f), + thin(false), + backlight(0.f), + thickness(1.f) + {} +}; +} // namespace ispc +#else +}; +#endif // __cplusplus diff --git a/modules/cpu/render/materials/ThinGlass.cpp b/modules/cpu/render/materials/ThinGlass.cpp index e692900e00..e099a40210 100644 --- a/modules/cpu/render/materials/ThinGlass.cpp +++ b/modules/cpu/render/materials/ThinGlass.cpp @@ -10,7 +10,9 @@ namespace pathtracer { ThinGlass::ThinGlass() { - ispcEquivalent = ispc::PathTracer_ThinGlass_create(); + getSh()->super.type = ispc::MATERIAL_TYPE_THINGLASS; + getSh()->super.getBSDF = ispc::ThinGlass_getBSDF_addr(); + getSh()->super.getTransparency = ispc::ThinGlass_getTransparency_addr(); } std::string ThinGlass::toString() const @@ -26,12 +28,13 @@ void ThinGlass::commit() const float attenuationDistance = getParam("attenuationDistance", 1.f); const float thickness = getParam("thickness", 1.f); - ispc::PathTracer_ThinGlass_set(getIE(), - eta, - (const ispc::vec3f &)attenuationColor, - attenuationColor.tex, - attenuationDistance, - thickness); + getSh()->eta = rcp(eta); + getSh()->attenuationScale = + thickness * rcp(std::max(attenuationDistance, EPS)); + const vec3f &acf = attenuationColor.factor; + getSh()->attenuation = + vec3f(log(acf.x), log(acf.y), log(acf.z)) * getSh()->attenuationScale; + getSh()->attenuationColorMap = attenuationColor.tex; } } // namespace pathtracer diff --git a/modules/cpu/render/materials/ThinGlass.h b/modules/cpu/render/materials/ThinGlass.h index d0790dd372..6fbf60260a 100644 --- a/modules/cpu/render/materials/ThinGlass.h +++ b/modules/cpu/render/materials/ThinGlass.h @@ -1,12 +1,14 @@ -// Copyright 2020 Intel Corporation +// Copyright 2020-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "render/Material.h" +#include "ThinGlassShared.h" + namespace ospray { namespace pathtracer { -struct ThinGlass : public ospray::Material +struct ThinGlass : public AddStructShared { ThinGlass(); diff --git a/modules/cpu/render/materials/ThinGlass.ispc b/modules/cpu/render/materials/ThinGlass.ispc index af205b9b71..3757e5c6d8 100644 --- a/modules/cpu/render/materials/ThinGlass.ispc +++ b/modules/cpu/render/materials/ThinGlass.ispc @@ -1,22 +1,11 @@ // Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 -#include "../bsdfs/RobustThinDielectric.ih" -#include "Material.ih" +#include "render/Material.ih" +#include "render/bsdfs/RobustThinDielectric.ih" #include "texture/TextureParam.ih" -struct ThinGlass -{ - PathTraceMaterial super; - - float eta; //!< reciprocal refraction index of internal medium - // assumed to be <=1 - vec3f attenuation; //!< negative Napierian attenuation coefficient, - // i.e. wrt. the natural base e - float attenuationScale; //!< factor to scale attenuation from texture due to - // thickness and attenuationDistance - TextureParam attenuationColorMap; -}; +#include "ThinGlassShared.h" /////////////////////////////////////////////////////////////////////////////// // Implementation @@ -35,8 +24,7 @@ static inline const vec3f getAttenuation( return attenuation; } -const varying BSDF *varying ThinGlass_getBSDF( - const PathTraceMaterial *uniform super, +const varying BSDF *varying ThinGlass_getBSDF(const Material *uniform super, ShadingContext *uniform ctx, const DifferentialGeometry &dg, const Ray &ray, @@ -49,7 +37,7 @@ const varying BSDF *varying ThinGlass_getBSDF( ctx, frame, self->eta, getAttenuation(self, dg)); } -vec3f ThinGlass_getTransparency(const PathTraceMaterial *uniform material, +vec3f ThinGlass_getTransparency(const Material *uniform material, const DifferentialGeometry &dg, const Ray &ray, const Medium ¤tMedium) @@ -64,27 +52,12 @@ vec3f ThinGlass_getTransparency(const PathTraceMaterial *uniform material, /////////////////////////////////////////////////////////////////////////////// // External API -export void PathTracer_ThinGlass_set(void *uniform _self, - const uniform float ior, - const uniform vec3f &attenuationColor, - const uniform TextureParam &attenuationColorMap, - const uniform float attenuationDistance, - const uniform float thickness) +export void *uniform ThinGlass_getBSDF_addr() { - ThinGlass *uniform self = (ThinGlass * uniform) _self; - self->eta = rcp(ior); - self->attenuationScale = thickness * rcp(max(attenuationDistance, EPS)); - self->attenuation = logf(attenuationColor) * self->attenuationScale; - self->attenuationColorMap = attenuationColorMap; + return ThinGlass_getBSDF; } -export void *uniform PathTracer_ThinGlass_create() +export void *uniform ThinGlass_getTransparency_addr() { - ThinGlass *uniform self = uniform new ThinGlass; - PathTraceMaterial_Constructor( - &self->super, MATERIAL_TYPE_THINGLASS, ThinGlass_getBSDF, ThinGlass_getTransparency); - PathTracer_ThinGlass_set( - self, 1.5f, make_vec3f(1.f), make_TextureParam(), 1.f, 1.f); - - return self; + return ThinGlass_getTransparency; } diff --git a/modules/cpu/render/materials/ThinGlassShared.h b/modules/cpu/render/materials/ThinGlassShared.h new file mode 100644 index 0000000000..63db3ff129 --- /dev/null +++ b/modules/cpu/render/materials/ThinGlassShared.h @@ -0,0 +1,31 @@ +// Copyright 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "render/MaterialShared.h" +#include "texture/TextureParamShared.h" + +#ifdef __cplusplus +namespace ispc { +#endif // __cplusplus + +struct ThinGlass +{ + Material super; + + float eta; // reciprocal refraction index of internal medium + // assumed to be <=1 + vec3f attenuation; // negative Napierian attenuation coefficient, + // i.e. wrt. the natural base e + float attenuationScale; // factor to scale attenuation from texture due to + // thickness and attenuationDistance + TextureParam attenuationColorMap; + +#ifdef __cplusplus + ThinGlass() : eta(1.5f), attenuation(0.f), attenuationScale(1.f) {} +}; +} // namespace ispc +#else +}; +#endif // __cplusplus diff --git a/modules/cpu/render/materials/Velvet.cpp b/modules/cpu/render/materials/Velvet.cpp index 14463e8f51..81a02d3edb 100644 --- a/modules/cpu/render/materials/Velvet.cpp +++ b/modules/cpu/render/materials/Velvet.cpp @@ -8,14 +8,15 @@ namespace ospray { namespace pathtracer { -std::string Velvet::toString() const +Velvet::Velvet() { - return "ospray::pathtracer::Velvet"; + getSh()->super.type = ispc::MATERIAL_TYPE_VELVET; + getSh()->super.getBSDF = ispc::Velvet_getBSDF_addr(); } -Velvet::Velvet() +std::string Velvet::toString() const { - ispcEquivalent = ispc::PathTracer_Velvet_create(); + return "ospray::pathtracer::Velvet"; } void Velvet::commit() @@ -27,11 +28,10 @@ void Velvet::commit() float horizonScatteringFallOff = getParam("horizonScatteringFallOff", 10); - ispc::PathTracer_Velvet_set(getIE(), - (const ispc::vec3f &)reflectance, - (const ispc::vec3f &)horizonScatteringColor, - horizonScatteringFallOff, - backScattering); + getSh()->reflectance = reflectance; + getSh()->backScattering = backScattering; + getSh()->horizonScatteringColor = horizonScatteringColor; + getSh()->horizonScatteringFallOff = horizonScatteringFallOff; } } // namespace pathtracer diff --git a/modules/cpu/render/materials/Velvet.h b/modules/cpu/render/materials/Velvet.h index 483ef9cf9b..1f55308bcf 100644 --- a/modules/cpu/render/materials/Velvet.h +++ b/modules/cpu/render/materials/Velvet.h @@ -1,12 +1,14 @@ -// Copyright 2020 Intel Corporation +// Copyright 2020-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "render/Material.h" +#include "VelvetShared.h" + namespace ospray { namespace pathtracer { -struct Velvet : public ospray::Material +struct Velvet : public AddStructShared { Velvet(); diff --git a/modules/cpu/render/materials/Velvet.ispc b/modules/cpu/render/materials/Velvet.ispc index 53c7dcabef..036a6b305f 100644 --- a/modules/cpu/render/materials/Velvet.ispc +++ b/modules/cpu/render/materials/Velvet.ispc @@ -1,35 +1,18 @@ // Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 -#include "../bsdfs/Minneart.ih" -#include "../bsdfs/MultiBSDF.ih" -#include "../bsdfs/Velvety.ih" -#include "Material.ih" +#include "render/Material.ih" +#include "render/bsdfs/Minneart.ih" +#include "render/bsdfs/MultiBSDF.ih" +#include "render/bsdfs/Velvety.ih" -struct Velvet -{ - PathTraceMaterial super; - - /*! Diffuse reflectance of the surface. The range is from 0 - * (black) to 1 (white). */ - vec3f reflectance; - - /*! Amount of back scattering. The range is from 0 (no back - * scattering) to inf (maximum back scattering). */ - float backScattering; - - /*! Color of horizon scattering. */ - vec3f horizonScatteringColor; - - /*! Fall-off of horizon scattering. */ - float horizonScatteringFallOff; -}; +#include "VelvetShared.h" /////////////////////////////////////////////////////////////////////////////// // Implementation const varying BSDF *varying Velvet_getBSDF( - const uniform PathTraceMaterial *uniform super, + const uniform Material *uniform super, uniform ShadingContext *uniform ctx, const DifferentialGeometry &dg, const Ray &ray, @@ -57,25 +40,10 @@ const varying BSDF *varying Velvet_getBSDF( return bsdf; } -export void *uniform PathTracer_Velvet_create() -{ - uniform Velvet *uniform self = uniform new uniform Velvet; - return self; -} - /////////////////////////////////////////////////////////////////////////////// // External API -export void *uniform PathTracer_Velvet_set(void *uniform _self, - const uniform vec3f &reflectance, - const uniform vec3f &horizonScatteringColor, - uniform float horizonScatteringFallOff, - uniform float backScattering) +export void *uniform Velvet_getBSDF_addr() { - uniform Velvet *uniform self = (uniform Velvet * uniform) _self; - self->reflectance = reflectance; - self->horizonScatteringColor = horizonScatteringColor; - self->horizonScatteringFallOff = horizonScatteringFallOff; - self->backScattering = backScattering; - PathTraceMaterial_Constructor(&self->super, MATERIAL_TYPE_VELVET, Velvet_getBSDF); + return Velvet_getBSDF; } diff --git a/modules/cpu/render/materials/VelvetShared.h b/modules/cpu/render/materials/VelvetShared.h new file mode 100644 index 0000000000..a41636a578 --- /dev/null +++ b/modules/cpu/render/materials/VelvetShared.h @@ -0,0 +1,41 @@ +// Copyright 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "render/MaterialShared.h" + +#ifdef __cplusplus +namespace ispc { +#endif // __cplusplus + +struct Velvet +{ + Material super; + + // Diffuse reflectance of the surface. The range is from 0 + // (black) to 1 (white). + vec3f reflectance; + + // Amount of back scattering. The range is from 0 (no back + // scattering) to inf (maximum back scattering). + float backScattering; + + // Color of horizon scattering. + vec3f horizonScatteringColor; + + // Fall-off of horizon scattering. + float horizonScatteringFallOff; + +#ifdef __cplusplus + Velvet() + : reflectance(.4f, 0.f, 0.f), + backScattering(.5f), + horizonScatteringColor(.75f, .1f, .1f), + horizonScatteringFallOff(10) + {} +}; +} // namespace ispc +#else +}; +#endif // __cplusplus diff --git a/modules/cpu/render/pathtracer/GeometryLight.ispc b/modules/cpu/render/pathtracer/GeometryLight.ispc index e4a4a37123..db855f106d 100644 --- a/modules/cpu/render/pathtracer/GeometryLight.ispc +++ b/modules/cpu/render/pathtracer/GeometryLight.ispc @@ -5,7 +5,7 @@ #include "render/pathtracer/PathStructs.ih" #include "render/pathtracer/PathTracerUtil.ih" -#include "render/materials/Material.ih" +#include "render/Material.ih" #include "geometry/GeometricModel.ih" #include "lights/Light.ih" @@ -19,7 +19,7 @@ vec3f evaluateGeometryLights(const PathContext &pathContext, Ray &ray) { vec3f L = make_vec3f(0.f); - PathTraceMaterial *material = (PathTraceMaterial *)pathVertex.dg.material; + Material *material = (Material *)pathVertex.dg.material; foreach_unique (m in material) { if (m != NULL && reduce_max(m->emission) > 0.f) { // at the moment we only support uniform light selection @@ -105,8 +105,8 @@ Light_SampleRes GeometryLight_sample(const Light *uniform super, res.pdf = self->pdf * sqr(dist) / abs(cosd); // get radiance - PathTraceMaterial *mat = (PathTraceMaterial *)GeometricModel_getMaterial( - model, dg.renderer, primID); + Material *mat = + (Material *)GeometricModel_getMaterial(model, dg.renderer, primID); const vec3f radiance = mat ? mat->emission : make_vec3f(0.f); res.weight = radiance * rcp(res.pdf); @@ -150,8 +150,8 @@ export void *uniform GeometryLight_create( self->primIDs = uniform new uniform int32[geo->numPrimitives]; uniform int32 numEmissivePrims = 0; foreach (primID = 0 ... geo->numPrimitives) { - PathTraceMaterial *mat = (PathTraceMaterial *)GeometricModel_getMaterial( - model, renderer, primID); + Material *mat = + (Material *)GeometricModel_getMaterial(model, renderer, primID); const vec3f emission = mat->emission; if (reduce_max(emission) > 0.f) { int offset = exclusive_scan_add(1); diff --git a/modules/cpu/render/pathtracer/PathSampler.ispc b/modules/cpu/render/pathtracer/PathSampler.ispc index ec68349476..20cb6ef57a 100644 --- a/modules/cpu/render/pathtracer/PathSampler.ispc +++ b/modules/cpu/render/pathtracer/PathSampler.ispc @@ -12,9 +12,9 @@ #include "render/pathtracer/VirtualLight.ih" #include "common/Intersect.ih" +#include "render/Material.ih" #include "render/bsdfs/BSDF.ih" #include "render/bsdfs/MicrofacetAlbedoTables.ih" -#include "render/materials/Material.ih" #include "render/materials/Medium.ih" #include "render/pathtracer/volumes/VolumeSampler.ih" @@ -165,7 +165,7 @@ void samplePath(const PathContext &pathContext, && pathVertex.type != VOLUME) { pathState.contribution = pathState.contribution + evaluateVirtualLights( - pathContext, pathState, lastVertex, pathVertex, ray); + pathContext, pathState, lastVertex, pathVertex, ray); } if (pathVertex.type == ENVIRONMENT) @@ -194,7 +194,7 @@ void samplePath(const PathContext &pathContext, // shade surface ShadingContext_Constructor(&ctx); if (pathVertex.type == SURFACE) { - PathTraceMaterial *material = (PathTraceMaterial *)pathVertex.dg.material; + Material *material = (Material *)pathVertex.dg.material; foreach_unique (m in material) { if (m != NULL) { pathVertex.bsdf = @@ -253,8 +253,7 @@ void samplePath(const PathContext &pathContext, // TODO: support nested dielectrics if (fs.type & SCATTERING_TRANSMISSION) { ray_org = ray_org - (2.0f * pathVertex.dg.epsilon) * pathVertex.dg.Ng; - PathTraceMaterial *material = - (PathTraceMaterial *)pathVertex.dg.material; + Material *material = (Material *)pathVertex.dg.material; foreach_unique (m in material) { if (m != NULL) m->selectNextMedium(m, pathVertex.dg, pathState.currentMedium); diff --git a/modules/cpu/render/pathtracer/PathTracer.cpp b/modules/cpu/render/pathtracer/PathTracer.cpp index 1a2b17a9fa..4960062f57 100644 --- a/modules/cpu/render/pathtracer/PathTracer.cpp +++ b/modules/cpu/render/pathtracer/PathTracer.cpp @@ -7,9 +7,9 @@ #include "common/Instance.h" #include "geometry/GeometricModel.h" #include "lights/Light.h" +#include "render/Material.h" // ispc exports #include "common/World_ispc.h" -#include "render/materials/Material_ispc.h" #include "render/pathtracer/GeometryLight_ispc.h" #include "render/pathtracer/PathTracer_ispc.h" // std @@ -44,7 +44,7 @@ void PathTracer::generateGeometryLights( // check whether the model has any emissive materials bool hasEmissive = false; for (auto mat : model->ispcMaterialPtrs) { - if (mat && ispc::PathTraceMaterial_isEmissive(mat)) { + if (mat && ((ispc::Material *)mat)->isEmissive()) { hasEmissive = true; break; } @@ -54,8 +54,7 @@ void PathTracer::generateGeometryLights( if (numRendererMaterials > 0 && model->ispcMaterialPtrs.size() == 0) for (auto matIdx : model->materialData->as()) if (matIdx < numRendererMaterials - && ispc::PathTraceMaterial_isEmissive( - ispcMaterialPtrs[matIdx])) { + && ((ispc::Material *)ispcMaterialPtrs[matIdx])->isEmissive()) { hasEmissive = true; break; } diff --git a/modules/cpu/render/pathtracer/PathTracer.ispc b/modules/cpu/render/pathtracer/PathTracer.ispc index 354d7f0967..ad4c10ad21 100644 --- a/modules/cpu/render/pathtracer/PathTracer.ispc +++ b/modules/cpu/render/pathtracer/PathTracer.ispc @@ -10,10 +10,11 @@ #include "lights/Light.ih" #include "render/bsdfs/BSDF.ih" #include "render/bsdfs/MicrofacetAlbedoTables.ih" -#include "render/materials/Material.ih" #include "render/materials/Medium.ih" #include "volumes/VolumeSampler.ih" +#include "render/MaterialShared.h" + #include "camera/Camera.ih" #include "fb/LocalFB.ih" #include "math/Distribution1D.ih" diff --git a/modules/cpu/render/pathtracer/TransparentShadow.ispc b/modules/cpu/render/pathtracer/TransparentShadow.ispc index 4596c3a159..9c4368bdcb 100644 --- a/modules/cpu/render/pathtracer/TransparentShadow.ispc +++ b/modules/cpu/render/pathtracer/TransparentShadow.ispc @@ -4,7 +4,7 @@ #include "render/pathtracer/PathTracer.ih" #include "render/pathtracer/TransparentShadow.ih" -#include "render/materials/Material.ih" +#include "render/Material.ih" #include "render/materials/Medium.ih" #include "common/Ray.ih" @@ -34,8 +34,7 @@ vec3f transparentShadow(const uniform PathTracer *uniform self, DG_NS | DG_NG | DG_FACEFORWARD | DG_NORMALIZE | DG_TEXCOORD | DG_COLOR | DG_MOTIONBLUR); - uniform PathTraceMaterial *material = - (uniform PathTraceMaterial *)dg.material; + uniform Material *material = (uniform Material *)dg.material; vec3f transparency; foreach_unique (m in material) if (m != NULL) diff --git a/modules/cpu/render/scivis/surfaces.ih b/modules/cpu/render/scivis/surfaces.ih index 8dc6b6b0e5..5ea5ba46f1 100644 --- a/modules/cpu/render/scivis/surfaces.ih +++ b/modules/cpu/render/scivis/surfaces.ih @@ -50,7 +50,7 @@ inline SciVisBSDF evalMaterial(const DifferentialGeometry &dg) res.opacity = dg.color.w; foreach_unique (m in mat) { - if (m != NULL && m->super.material.type == MATERIAL_TYPE_OBJ) { + if (m != NULL && m->super.type == MATERIAL_TYPE_OBJ) { float d = m->d * get1f(m->dMap, dg, 1.f) * dg.color.w; res.diffuse = res.diffuse * m->Kd; if (valid(m->KdMap)) { diff --git a/modules/cpu/texture/Texture.cpp b/modules/cpu/texture/Texture.cpp index 093478d36a..013ea22f82 100644 --- a/modules/cpu/texture/Texture.cpp +++ b/modules/cpu/texture/Texture.cpp @@ -1,4 +1,4 @@ -// Copyright 2009-2020 Intel Corporation +// Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "Texture.h" diff --git a/modules/cpu/texture/Texture.h b/modules/cpu/texture/Texture.h index 25d4f163fc..5e8cfd6b43 100644 --- a/modules/cpu/texture/Texture.h +++ b/modules/cpu/texture/Texture.h @@ -1,14 +1,18 @@ -// Copyright 2009-2020 Intel Corporation +// Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once #include "common/Managed.h" +#include "common/StructShared.h" #include "common/Util.h" +#include "TextureShared.h" + namespace ospray { -struct OSPRAY_SDK_INTERFACE Texture : public ManagedObject +struct OSPRAY_SDK_INTERFACE Texture + : AddStructShared { Texture(); virtual ~Texture() override = default; diff --git a/modules/cpu/texture/Texture.ih b/modules/cpu/texture/Texture.ih new file mode 100644 index 0000000000..6ea2e9c1d7 --- /dev/null +++ b/modules/cpu/texture/Texture.ih @@ -0,0 +1,103 @@ +// Copyright 2009-2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "../common/DifferentialGeometry.ih" + +#include "TextureShared.h" + +inline uniform bool hasAlpha(const uniform Texture *uniform self) +{ + return (self == NULL) ? false : self->hasAlpha; +} + +/*! helper function that returns the sampled value for the first + channel of the given texture + + Right now, this function always asks the texture for all four + channels, and then discards all but one; later implementations may + have specialized 'get1f' methods with the texture + + \note self may NOT be NULL! +*/ +inline float get1f( + const uniform Texture *uniform self, const varying DifferentialGeometry &dg) +{ + vec4f ret = self->get(self, dg); + return ret.x; +} + +/*! helper function that returns the sampled value for the first three + channels of the given texture + + Right now, this function always asks the texture for all four + channels, and then discards all but one; later implementations may + have specialized 'get3f' methods with the texture + + \note self may NOT be NULL! +*/ +inline vec3f get3f( + const uniform Texture *uniform self, const varying DifferentialGeometry &dg) +{ + vec4f ret = self->get(self, dg); + return make_vec3f(ret); +} + +/*! helper function that returns the sampled value of the four + channels of the given texture. + + Note that it's up to the texture to define clearly what happens if + we ask for four channels even if the texture has less physical + channels. + + \note self may NOT be NULL! +*/ +inline vec4f get4f( + const uniform Texture *uniform self, const varying DifferentialGeometry &dg) +{ + return self->get(self, dg); +} + +/*! helper function that returns the sampled values interpreted as a normal */ +inline vec3f getNormal( + const uniform Texture *uniform self, const varying DifferentialGeometry &dg) +{ + if (self == NULL) + return make_vec3f(0.f, 0.f, 1.f); + else + return self->getNormal(self, dg); +} + +/*! helper function: get1f() with a default value if the texture is NULL */ +inline float get1f(const uniform Texture *uniform self, + const varying DifferentialGeometry &dg, + const varying float defaultValue) +{ + if (self == NULL) + return defaultValue; + else + return get1f(self, dg); +} + +/*! helper function: get3f() with a default value if the texture is NULL */ +inline vec3f get3f(const uniform Texture *uniform self, + const varying DifferentialGeometry &dg, + const varying vec3f defaultValue) +{ + if (self == NULL) + return defaultValue; + else + return get3f(self, dg); +} + +/*! helper function: get4f() with a default value if the texture is NULL */ +inline vec4f get4f(const uniform Texture *uniform self, + const varying DifferentialGeometry &dg, + const varying vec4f defaultValue) +{ + if (self == NULL) + return defaultValue; + else + return get4f(self, dg); +} diff --git a/modules/cpu/texture/Texture2D.cpp b/modules/cpu/texture/Texture2D.cpp index 577a1981ff..29e0b1eb3a 100644 --- a/modules/cpu/texture/Texture2D.cpp +++ b/modules/cpu/texture/Texture2D.cpp @@ -1,4 +1,4 @@ -// Copyright 2009-2020 Intel Corporation +// Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "Texture2D.h" @@ -7,6 +7,35 @@ #include "../common/Data.h" +namespace ispc { + +void Texture2D::Set(const vec2i &aSize, + void *aData, + OSPTextureFormat type, + OSPTextureFilter flags) +{ + size = aSize; + + // Due to float rounding frac(x) can be exactly 1.0f (e.g. for very small + // negative x), although it should be strictly smaller than 1.0f. We handle + // this case by having sizef slightly smaller than size, such that + // frac(x)*sizef is always < size. + sizef = + vec2f(nextafter((float)size.x, -1.0f), nextafter((float)size.y, -1.0f)); + halfTexel = vec2f(0.5f / size.x, 0.5f / size.y); + data = aData; + super.get = + ispc::Texture2D_get_addr(type, flags & OSP_TEXTURE_FILTER_NEAREST); + super.getNormal = + ispc::Texture2D_getN_addr(type, flags & OSP_TEXTURE_FILTER_NEAREST); + super.hasAlpha = type == OSP_TEXTURE_RGBA8 || type == OSP_TEXTURE_SRGBA + || type == OSP_TEXTURE_RA8 || type == OSP_TEXTURE_LA8 + || type == OSP_TEXTURE_RGBA32F || type == OSP_TEXTURE_RGBA16 + || type == OSP_TEXTURE_RA16; +} + +} // namespace ispc + namespace ospray { std::string Texture2D::toString() const @@ -48,9 +77,7 @@ void Texture2D::commit() + "' does not match type of 'data'='" + stringFor(texData->type) + "'!"); - ispc::delete_uniform(ispcEquivalent); - this->ispcEquivalent = ispc::Texture2D_create( - (ispc::vec2i &)size, texData->data(), format, filter); + getSh()->Set(size, texData->data(), format, filter); } } // namespace ospray diff --git a/modules/cpu/texture/Texture2D.h b/modules/cpu/texture/Texture2D.h index e4c95408aa..de3dec7293 100644 --- a/modules/cpu/texture/Texture2D.h +++ b/modules/cpu/texture/Texture2D.h @@ -1,15 +1,19 @@ -// Copyright 2009-2019 Intel Corporation +// Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once #include "Texture.h" #include "common/Data.h" +#include "common/StructShared.h" + +#include "Texture2DShared.h" namespace ospray { -/*! \brief A Texture defined through a 2D Image. */ -struct OSPRAY_SDK_INTERFACE Texture2D : public Texture +// A Texture defined through a 2D Image +struct OSPRAY_SDK_INTERFACE Texture2D + : public AddStructShared { virtual ~Texture2D() override = default; diff --git a/modules/cpu/texture/Texture2D.ih b/modules/cpu/texture/Texture2D.ih index 59d4da9f28..23f2b9bb57 100644 --- a/modules/cpu/texture/Texture2D.ih +++ b/modules/cpu/texture/Texture2D.ih @@ -3,29 +3,9 @@ #pragma once -#include "../common/DifferentialGeometry.ih" -#include "ospray/OSPEnums.h" -#include "rkcommon/math/vec.ih" +#include "Texture.ih" -struct Texture2D; - -typedef varying vec4f (*Texture2D_get)(const uniform Texture2D *uniform self, - const varying DifferentialGeometry &dg); - -typedef varying vec3f (*Texture2D_getN)(const uniform Texture2D *uniform self, - const varying DifferentialGeometry &dg); - -struct Texture2D -{ - vec2i size; - vec2f sizef; // size, as floats; slightly smaller than 'size' to avoid range - // checks - vec2f halfTexel; // 0.5/size, needed for bilinear filtering and clamp-to-edge - Texture2D_get get; - Texture2D_getN getNormal; - void *data; - bool hasAlpha; // 4 channel texture? -}; +#include "Texture2DShared.h" // XXX won't work with MIPmapping: clean implementation with clamping on integer // coords needed then @@ -34,99 +14,49 @@ inline vec2f clamp2edge(const uniform Texture2D *uniform self, const vec2f p) return clamp(p, self->halfTexel, 1.0f - self->halfTexel); } -inline uniform bool hasAlpha(const uniform Texture2D *uniform self) -{ - return (self == NULL) ? false : self->hasAlpha; -} - -/*! helper function that returns the sampled value for the first - channel of the given texture - - Right now, this function always asks the texture for all four - channels, and then discards all but one; later implementations may - have specialized 'get1f' methods with the texture - - \note self may NOT be NULL! -*/ inline float get1f(const uniform Texture2D *uniform self, const varying DifferentialGeometry &dg) { - vec4f ret = self->get(self, dg); - return ret.x; + return get1f(&self->super, dg); } -/*! helper function that returns the sampled value for the first three - channels of the given texture - - Right now, this function always asks the texture for all four - channels, and then discards all but one; later implementations may - have specialized 'get3f' methods with the texture - - \note self may NOT be NULL! -*/ inline vec3f get3f(const uniform Texture2D *uniform self, const varying DifferentialGeometry &dg) { - vec4f ret = self->get(self, dg); - return make_vec3f(ret); + return get3f(&self->super, dg); } -/*! helper function that returns the sampled value of the four - channels of the given texture. - - Note that it's up to the texture to define clearly what happens if - we ask for four channels even if the texture has less physical - channels. - - \note self may NOT be NULL! -*/ inline vec4f get4f(const uniform Texture2D *uniform self, const varying DifferentialGeometry &dg) { - return self->get(self, dg); + return get4f(&self->super, dg); } -/*! helper function that returns the sampled values interpreted as a normal */ inline vec3f getNormal(const uniform Texture2D *uniform self, const varying DifferentialGeometry &dg) { - if (self == NULL) - return make_vec3f(0.f, 0.f, 1.f); - else - return self->getNormal(self, dg); + return getNormal(&self->super, dg); } -/*! helper function: get1f() with a default value if the texture is NULL */ inline float get1f(const uniform Texture2D *uniform self, const varying DifferentialGeometry &dg, const varying float defaultValue) { - if (self == NULL) - return defaultValue; - else - return get1f(self, dg); + return get1f(&self->super, dg, defaultValue); } -/*! helper function: get3f() with a default value if the texture is NULL */ inline vec3f get3f(const uniform Texture2D *uniform self, const varying DifferentialGeometry &dg, const varying vec3f defaultValue) { - if (self == NULL) - return defaultValue; - else - return get3f(self, dg); + return get3f(&self->super, dg, defaultValue); } -/*! helper function: get4f() with a default value if the texture is NULL */ inline vec4f get4f(const uniform Texture2D *uniform self, const varying DifferentialGeometry &dg, const varying vec4f defaultValue) { - if (self == NULL) - return defaultValue; - else - return get4f(self, dg); + return get4f(&self->super, dg, defaultValue); } #if 0 // crashes ISPC! diff --git a/modules/cpu/texture/Texture2D.ispc b/modules/cpu/texture/Texture2D.ispc index 86b4de625e..b8184d937f 100644 --- a/modules/cpu/texture/Texture2D.ispc +++ b/modules/cpu/texture/Texture2D.ispc @@ -1,7 +1,8 @@ -// Copyright 2009-2020 Intel Corporation +// Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "Texture2D.ih" +#include "ospray/OSPEnums.h" // Low-level texel accessors ////////////////////////////////////////////////////////////////////////////// @@ -260,7 +261,7 @@ inline vec4f bilerp(const vec2f frac, __foreach_fetcher(__define_tex_get); -static uniform Texture2D_get Texture2D_get_addr( +export void *uniform Texture2D_get_addr( const uniform uint32 type, const uniform bool filter_nearest) { switch (type) { @@ -294,7 +295,7 @@ __define_tex_getN(RGBA32F, 2.f); __define_tex_getN(RGB16, (65535.f / 32767.f)); __define_tex_getN(RGBA16, (65535.f / 32767.f)); -static uniform Texture2D_getN Texture2D_getN_addr( +export void *uniform Texture2D_getN_addr( const uniform uint32 type, const uniform bool filter_nearest) { switch (type) { @@ -321,32 +322,3 @@ static uniform Texture2D_getN Texture2D_getN_addr( #undef BYTE2FLOAT #undef SHORT2FLOAT - -// Exports (called from C++) -////////////////////////////////////////////////////////////////////////////// - -export void *uniform Texture2D_create(uniform vec2i &size, - void *uniform data, - uniform uint32 type, - uniform uint32 flags) -{ - Texture2D *uniform self = uniform new Texture2D; - self->size = size; - - // Due to float rounding frac(x) can be exactly 1.0f (e.g. for very small - // negative x), although it should be strictly smaller than 1.0f. We handle - // this case by having sizef slightly smaller than size, such that - // frac(x)*sizef is always < size. - self->sizef = make_vec2f( - nextafter((float)size.x, -1.0f), nextafter((float)size.y, -1.0f)); - self->halfTexel = make_vec2f(0.5f / size.x, 0.5f / size.y); - self->data = data; - self->get = Texture2D_get_addr(type, flags & OSP_TEXTURE_FILTER_NEAREST); - self->getNormal = - Texture2D_getN_addr(type, flags & OSP_TEXTURE_FILTER_NEAREST); - self->hasAlpha = type == OSP_TEXTURE_RGBA8 || type == OSP_TEXTURE_SRGBA - || type == OSP_TEXTURE_RA8 || type == OSP_TEXTURE_LA8 - || type == OSP_TEXTURE_RGBA32F || type == OSP_TEXTURE_RGBA16 - || type == OSP_TEXTURE_RA16; - return self; -} diff --git a/modules/cpu/texture/Texture2DShared.h b/modules/cpu/texture/Texture2DShared.h new file mode 100644 index 0000000000..9f4633cdb8 --- /dev/null +++ b/modules/cpu/texture/Texture2DShared.h @@ -0,0 +1,33 @@ +// Copyright 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "TextureShared.h" + +#ifdef __cplusplus +using namespace rkcommon::math; +namespace ispc { +#endif // __cplusplus + +struct Texture2D +{ + Texture super; + + vec2i size; + vec2f sizef; // size, as floats; slightly smaller than 'size' to avoid range + // checks + vec2f halfTexel; // 0.5/size, needed for bilinear filtering and clamp-to-edge + void *data; + +#ifdef __cplusplus + Texture2D() : size(0), sizef(0.f), halfTexel(0.f), data(nullptr) {} + void Set(const vec2i &aSize, + void *aData, + OSPTextureFormat type, + OSPTextureFilter flags); +}; +} // namespace ispc +#else +}; +#endif // __cplusplus diff --git a/modules/cpu/texture/TextureParam.ih b/modules/cpu/texture/TextureParam.ih index 8bfe66ec61..cd5aacf585 100644 --- a/modules/cpu/texture/TextureParam.ih +++ b/modules/cpu/texture/TextureParam.ih @@ -1,37 +1,12 @@ -// Copyright 2009-2020 Intel Corporation +// Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once -#include "Texture2D.ih" +#include "Texture.ih" #include "math/AffineSpace.ih" -//! Texture2D including coordinate transformation, plus helpers - -enum TransformFlags -{ - TRANSFORM_FLAG_NONE = 0x0, - TRANSFORM_FLAG_2D = 0x1, - TRANSFORM_FLAG_3D = 0x2 -}; - -struct TextureParam -{ - Texture2D *ptr; - TransformFlags transformFlags; - affine2f xform2f; - affine3f xform3f; -}; - -inline uniform TextureParam make_TextureParam() -{ - uniform TextureParam t; - t.ptr = NULL; - t.transformFlags = TRANSFORM_FLAG_NONE; - t.xform2f = make_AffineSpace2f_identity(); - t.xform3f = make_AffineSpace3f_identity(); - return t; -} +#include "TextureParamShared.h" inline uniform bool valid(const uniform TextureParam uniform &tex) { diff --git a/modules/cpu/texture/TextureParam.ispc b/modules/cpu/texture/TextureParam.ispc deleted file mode 100644 index ddc815b166..0000000000 --- a/modules/cpu/texture/TextureParam.ispc +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2020 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 - -#include "TextureParam.ih" - -export void TextureParam_set(TextureParam *uniform self, - void *uniform ptr, - uniform TransformFlags transformFlags, - const uniform affine2f &xform2f, - const uniform affine3f &xform3f) -{ - self->ptr = (Texture2D * uniform) ptr; - self->transformFlags = transformFlags; - self->xform2f = xform2f; - self->xform3f = xform3f; -} diff --git a/modules/cpu/texture/TextureParamShared.h b/modules/cpu/texture/TextureParamShared.h new file mode 100644 index 0000000000..4083d278dc --- /dev/null +++ b/modules/cpu/texture/TextureParamShared.h @@ -0,0 +1,38 @@ +// Copyright 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "TextureShared.h" + +#ifdef __cplusplus +using namespace rkcommon::math; +namespace ispc { +#endif // __cplusplus + +enum TransformFlags +{ + TRANSFORM_FLAG_NONE = 0x0, + TRANSFORM_FLAG_2D = 0x1, + TRANSFORM_FLAG_3D = 0x2 +}; + +struct TextureParam +{ + Texture *ptr; + TransformFlags transformFlags; + affine2f xform2f; + affine3f xform3f; + +#ifdef __cplusplus + TextureParam() + : ptr(nullptr), + transformFlags(TRANSFORM_FLAG_NONE), + xform2f(one), + xform3f(one) + {} +}; +} // namespace ispc +#else +}; +#endif // __cplusplus diff --git a/modules/cpu/texture/TextureShared.h b/modules/cpu/texture/TextureShared.h new file mode 100644 index 0000000000..1e1a2e995b --- /dev/null +++ b/modules/cpu/texture/TextureShared.h @@ -0,0 +1,33 @@ +// Copyright 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#ifdef __cplusplus +namespace ispc { +typedef void *Texture_get; +typedef void *Texture_getN; +#else +struct Texture; +struct DifferentialGeometry; +typedef varying vec4f (*Texture_get)( + const Texture *uniform self, const varying DifferentialGeometry &dg); +typedef varying vec3f (*Texture_getN)( + const Texture *uniform self, const varying DifferentialGeometry &dg); +#endif // __cplusplus + +struct Texture +{ + Texture_get get; + Texture_getN getNormal; + bool hasAlpha; // 4 channel texture? + +#ifdef __cplusplus + Texture(bool hasAlpha = false) + : get(nullptr), getNormal(nullptr), hasAlpha(hasAlpha) + {} +}; +} // namespace ispc +#else +}; +#endif // __cplusplus diff --git a/modules/cpu/texture/TextureVolume.cpp b/modules/cpu/texture/TextureVolume.cpp index 2048f16746..50fb72ae3d 100644 --- a/modules/cpu/texture/TextureVolume.cpp +++ b/modules/cpu/texture/TextureVolume.cpp @@ -1,12 +1,9 @@ -// Copyright 2009-2020 Intel Corporation +// Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "TextureVolume.h" #include "texture/TextureVolume_ispc.h" -#include "common/Data.h" -#include "volume/transferFunction/TransferFunction.h" - namespace ospray { std::string TextureVolume::toString() const @@ -14,26 +11,8 @@ std::string TextureVolume::toString() const return "ospray::TextureVolume"; } -TextureVolume::~TextureVolume() -{ - release(); -} - -void TextureVolume::release() -{ - if (ispcEquivalent) { - ispc::TextureVolume_delete(ispcEquivalent); - ispcEquivalent = nullptr; - } - volume = nullptr; - transferFunction = nullptr; - volumetricModel = nullptr; -} - void TextureVolume::commit() { - release(); - volume = dynamic_cast(getParamObject("volume")); if (volume) { auto *transferFunction = @@ -41,16 +20,18 @@ void TextureVolume::commit() if (!transferFunction) { throw std::runtime_error(toString() + " must have 'transferFunction'"); } - ispcEquivalent = - ispc::TextureVolume_create(volume->getIE(), transferFunction->getIE()); + getSh()->volume = volume->getSh(); + getSh()->transferFunction = transferFunction->getSh(); } else { volumetricModel = dynamic_cast(getParamObject("volume")); if (!volumetricModel) { throw std::runtime_error(toString() + " must have 'volume' object"); } - ispcEquivalent = - ispc::TextureVolume_create_deprecated(volumetricModel->getIE()); + getSh()->volume = volumetricModel->getSh()->volume; + getSh()->transferFunction = volumetricModel->getSh()->transferFunction; } + getSh()->super.get = ispc::TextureVolume_get_addr(); + getSh()->super.getNormal = ispc::TextureVolume_getN_addr(); } } // namespace ospray diff --git a/modules/cpu/texture/TextureVolume.h b/modules/cpu/texture/TextureVolume.h index 7e1d6ed228..b28601a1ab 100644 --- a/modules/cpu/texture/TextureVolume.h +++ b/modules/cpu/texture/TextureVolume.h @@ -1,26 +1,27 @@ -// Copyright 2009-2020 Intel Corporation +// Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once #include "Texture.h" +#include "common/StructShared.h" #include "volume/Volume.h" #include "volume/VolumetricModel.h" #include "volume/transferFunction/TransferFunction.h" +#include "TextureVolumeShared.h" + namespace ospray { -/*! \brief A Texture defined through a 2D Image. */ -struct OSPRAY_SDK_INTERFACE TextureVolume : public Texture +// A Texture defined through a volume +struct OSPRAY_SDK_INTERFACE TextureVolume + : public AddStructShared { - virtual ~TextureVolume() override; - virtual std::string toString() const override; virtual void commit() override; private: - void release(); Ref volume; Ref transferFunction; diff --git a/modules/cpu/texture/TextureVolume.ispc b/modules/cpu/texture/TextureVolume.ispc index 36c4665bad..21c13fa0a3 100644 --- a/modules/cpu/texture/TextureVolume.ispc +++ b/modules/cpu/texture/TextureVolume.ispc @@ -1,36 +1,28 @@ -// Copyright 2009-2020 Intel Corporation +// Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 -#include "Texture2D.ih" +#include "Texture.ih" +#include "volume/Volume.ih" +#include "volume/transferFunction/TransferFunction.ih" -#include "volume/VolumetricModel.ih" - -struct TextureVolumeData -{ - const Volume *volume; //!< instanced Volume - - //! Color and opacity transfer function. - const TransferFunction *transferFunction; -}; +#include "TextureVolumeShared.h" vec4f TextureVolume_get( - const Texture2D *uniform self, const varying DifferentialGeometry &dg) + const TextureVolume *uniform self, const varying DifferentialGeometry &dg) { - const TextureVolumeData *uniform data = - (TextureVolumeData * uniform) self->data; - const Volume *uniform volume = data->volume; + const Volume *uniform volume = self->volume; const uniform box3f bounds = volume->boundingBox; if (box_contains(bounds, dg.lP)) { const float sample = Volume_getSample(volume, dg.lP); - const TransferFunction *uniform tfn = data->transferFunction; + const TransferFunction *uniform tfn = self->transferFunction; return tfn->get(tfn, sample); } else return make_vec4f(0.f); } vec3f TextureVolume_getN( - const Texture2D *uniform self, const varying DifferentialGeometry &dg) + const TextureVolume *uniform self, const varying DifferentialGeometry &dg) { // TODO return make_vec3f(0.f, 0.0f, 1.0f); @@ -38,39 +30,12 @@ vec3f TextureVolume_getN( /////////////////////////////////////////////////////////////////////////////// -export void *uniform TextureVolume_create_deprecated( - void *uniform _volumetricModel) -{ - Texture2D *uniform self = uniform new uniform Texture2D; - const VolumetricModel *uniform volumetricModel = - (VolumetricModel * uniform) _volumetricModel; - TextureVolumeData *uniform data = uniform new uniform TextureVolumeData; - data->volume = volumetricModel->volume; - data->transferFunction = volumetricModel->transferFunction; - self->data = data; - self->get = TextureVolume_get; - self->getNormal = TextureVolume_getN; - return self; -} - -export void *uniform TextureVolume_create( - void *uniform volume, void *uniform transferFunction) +export void *uniform TextureVolume_get_addr() { - Texture2D *uniform self = uniform new uniform Texture2D; - TextureVolumeData *uniform data = uniform new uniform TextureVolumeData; - data->volume = (const Volume *uniform)volume; - data->transferFunction = (const TransferFunction *uniform)transferFunction; - self->data = data; - self->get = TextureVolume_get; - self->getNormal = TextureVolume_getN; - return self; + return TextureVolume_get; } -export void TextureVolume_delete(void *uniform _self) +export void *uniform TextureVolume_getN_addr() { - if (_self) { - Texture2D *uniform self = (Texture2D * uniform) _self; - delete self->data; - } - delete _self; + return TextureVolume_getN; } diff --git a/modules/cpu/texture/TextureVolumeShared.h b/modules/cpu/texture/TextureVolumeShared.h new file mode 100644 index 0000000000..b43e2de5b4 --- /dev/null +++ b/modules/cpu/texture/TextureVolumeShared.h @@ -0,0 +1,30 @@ +// Copyright 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "TextureShared.h" + +#ifdef __cplusplus +namespace ispc { +#endif // __cplusplus + +struct Volume; +struct TransferFunction; + +struct TextureVolume +{ + Texture super; + + const Volume *volume; // instanced Volume + + // Color and opacity transfer function. + const TransferFunction *transferFunction; + +#ifdef __cplusplus + TextureVolume() : super(true), volume(nullptr), transferFunction(nullptr) {} +}; +} // namespace ispc +#else +}; +#endif // __cplusplus diff --git a/modules/cpu/volume/Volume.cpp b/modules/cpu/volume/Volume.cpp index 1920009a9c..89d584f0e2 100644 --- a/modules/cpu/volume/Volume.cpp +++ b/modules/cpu/volume/Volume.cpp @@ -5,7 +5,6 @@ #include "volume/Volume.h" #include "common/Data.h" #include "common/Util.h" -#include "transferFunction/TransferFunction.h" #include "volume/Volume_ispc.h" #include "openvkl/openvkl.h" @@ -27,7 +26,6 @@ Volume::Volume(const std::string &type) : vklType(type) throw std::runtime_error(toString() + " Open VKL has non-default configuration for VDB volumes."); - ispcEquivalent = ispc::Volume_createInstance_vklVolume(); managedObjectType = OSP_VOLUME; } @@ -80,9 +78,19 @@ void Volume::commit() vklSampler = vklNewSampler(vklVolume); vklCommit(vklSampler); - ispc::Volume_set(ispcEquivalent, embreeGeometry); - ispc::Volume_set_vklVolume( - ispcEquivalent, vklVolume, vklSampler, (ispc::box3f *)&bounds); + // Setup Embree user-defined geometry + rtcSetGeometryUserData(embreeGeometry, getSh()); + rtcSetGeometryUserPrimitiveCount(embreeGeometry, 1); + rtcSetGeometryBoundsFunction( + embreeGeometry, (RTCBoundsFunction)&ispc::Volume_embreeBounds, getSh()); + rtcSetGeometryIntersectFunction( + embreeGeometry, (RTCIntersectFunctionN)&ispc::Volume_intersect_kernel); + rtcCommitGeometry(embreeGeometry); + + // Initialize shared structure + getSh()->vklVolume = vklVolume; + getSh()->vklSampler = vklSampler; + getSh()->boundingBox = *(box3f *)&bounds; } void Volume::checkDataStride(const Data *data) const @@ -198,6 +206,11 @@ void Volume::setDevice(RTCDevice embreed, VKLDevice vkld) vklDevice = vkld; } +void Volume::setGeomID(int geomID) +{ + getSh()->volumeID = geomID; +} + OSPTYPEFOR_DEFINITION(Volume *); } // namespace ospray diff --git a/modules/cpu/volume/Volume.h b/modules/cpu/volume/Volume.h index 5ebe92bd20..0a5180889c 100644 --- a/modules/cpu/volume/Volume.h +++ b/modules/cpu/volume/Volume.h @@ -5,23 +5,25 @@ #include "ISPCDevice.h" #include "common/Managed.h" +#include "common/StructShared.h" // embree #include "embree3/rtcore.h" #include "openvkl/volume.h" +#include "volume/VolumeShared.h" namespace ospray { -struct OSPRAY_SDK_INTERFACE Volume : public ManagedObject +struct OSPRAY_SDK_INTERFACE Volume + : public AddStructShared { Volume(const std::string &vklType); ~Volume() override; std::string toString() const override; - void commit() override; - void setDevice(RTCDevice embreeDevice, VKLDevice vklDevice); + void setGeomID(int geomID); private: void checkDataStride(const Data *) const; @@ -33,7 +35,6 @@ struct OSPRAY_SDK_INTERFACE Volume : public ManagedObject friend struct VolumetricModel; // Data // - RTCGeometry embreeGeometry{nullptr}; VKLVolume vklVolume{nullptr}; VKLSampler vklSampler{nullptr}; diff --git a/modules/cpu/volume/Volume.ih b/modules/cpu/volume/Volume.ih index 69670521ca..a286157b17 100644 --- a/modules/cpu/volume/Volume.ih +++ b/modules/cpu/volume/Volume.ih @@ -3,43 +3,10 @@ #pragma once -#include "common/Ray.ih" -#include "math/AffineSpace.ih" -#include "rkcommon/math/box.ih" -// openvkl #include "openvkl/openvkl.isph" +#include "rkcommon/math/box.ih" -struct TransferFunction; - -//! \brief Variables and methods common to all subtypes of the Volume -//! class, an abstraction for the concrete object which performs the -//! volume sampling (this struct must be the first field of a struct -//! representing a "derived" class to allow casting to that class). -//! -struct Volume -{ - int32 volumeID; - - VKLVolume vklVolume; - VKLSampler vklSampler; - - //! Find the next isosurface hit point in the volume for ray casting based - //! renderers. - void (*intersectIsosurface)(const void *uniform _self, - uniform float *uniform isovalues, - uniform int numIsovalues, - uniform size_t geomID, - // ray.u contains ray-space intersection error, i.e. the epsilon - varying Ray &ray); - - /*! Bounding box for the volume in world coordinates. - This is an internal derived parameter and not meant to be - redefined externally. - */ - box3f boundingBox; -}; - -void Volume_Constructor(Volume *uniform volume); +#include "VolumeShared.h" // Helper functions /////////////////////////////////////////////////////////// diff --git a/modules/cpu/volume/Volume.ispc b/modules/cpu/volume/Volume.ispc index 7f90aabd98..d7dce9ae3c 100644 --- a/modules/cpu/volume/Volume.ispc +++ b/modules/cpu/volume/Volume.ispc @@ -2,12 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 #include "common/Intersect.ih" -#include "common/OSPCommon.ih" #include "common/VolumeIntervals.ih" #include "volume/Volume.ih" -unmasked void Volume_embreeBounds( - const RTCBoundsFunctionArguments *uniform args) +export void Volume_embreeBounds(const RTCBoundsFunctionArguments *uniform args) { Volume *uniform self = (Volume * uniform) args->geometryUserPtr; box3fa *uniform out = (box3fa * uniform) args->bounds_o; @@ -15,7 +13,7 @@ unmasked void Volume_embreeBounds( *out = make_box3fa(self->boundingBox); } -unmasked void Volume_intersect_kernel( +export void Volume_intersect_kernel( const RTCIntersectFunctionNArguments *uniform args) { // make sure to set the mask @@ -64,58 +62,3 @@ unmasked void Volume_intersect_kernel( vIntervals->numVolumeIntervals++; } } - -void Volume_Constructor(Volume *uniform self) -{ - self->boundingBox = make_box3f(make_vec3f(0.f), make_vec3f(1.f)); - self->volumeID = -1; - self->vklVolume = NULL; - self->vklSampler = NULL; -} - -export void Volume_getBoundingBox( - uniform box3f *uniform ret, void *uniform _self) -{ - uniform Volume *uniform self = (uniform Volume * uniform) _self; - - *ret = self->boundingBox; -} - -export void Volume_set(void *uniform _self, void *uniform _embreeGeometry) -{ - Volume *uniform self = (Volume * uniform) _self; - - RTCGeometry geom = (RTCGeometry)_embreeGeometry; - - rtcSetGeometryUserData(geom, self); - rtcSetGeometryUserPrimitiveCount(geom, 1); - rtcSetGeometryBoundsFunction( - geom, (uniform RTCBoundsFunction)&Volume_embreeBounds, self); - rtcSetGeometryIntersectFunction( - geom, (uniform RTCIntersectFunctionN)&Volume_intersect_kernel); - rtcCommitGeometry(geom); -} - -export void Volume_set_geomID(void *uniform _self, uniform int32 geomID) -{ - Volume *uniform self = (Volume * uniform) _self; - self->volumeID = geomID; -} - -export void *uniform Volume_createInstance_vklVolume() -{ - Volume *uniform self = uniform new Volume; - Volume_Constructor(self); - return self; -} - -export void Volume_set_vklVolume(void *uniform _self, - void *uniform vklVolume, - void *uniform vklSampler, - box3f *uniform bounds) -{ - Volume *uniform self = (Volume * uniform) _self; - self->vklVolume = (VKLVolume)vklVolume; - self->vklSampler = (VKLSampler)vklSampler; - self->boundingBox = *bounds; -} diff --git a/modules/cpu/volume/VolumeShared.h b/modules/cpu/volume/VolumeShared.h new file mode 100644 index 0000000000..5f49e9be31 --- /dev/null +++ b/modules/cpu/volume/VolumeShared.h @@ -0,0 +1,30 @@ +// Copyright 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#ifdef __cplusplus +using namespace rkcommon::math; +namespace ispc { +#endif // __cplusplus + +// Variables and methods common to all subtypes of the Volume +// class, an abstraction for the concrete object which performs the +// volume sampling (this struct must be the first field of a struct +// representing a "derived" class to allow casting to that class). +struct Volume +{ + int volumeID; + + VKLVolume vklVolume; + VKLSampler vklSampler; + + // Bounding box for the volume in world coordinates. + // This is an internal derived parameter and not meant to be + // redefined externally. + box3f boundingBox; +}; + +#ifdef __cplusplus +} // namespace ispc +#endif // __cplusplus diff --git a/modules/cpu/volume/VolumetricModel.cpp b/modules/cpu/volume/VolumetricModel.cpp index 119257295c..937c49649a 100644 --- a/modules/cpu/volume/VolumetricModel.cpp +++ b/modules/cpu/volume/VolumetricModel.cpp @@ -4,11 +4,6 @@ // ospray #include "VolumetricModel.h" #include "transferFunction/TransferFunction.h" -// openvkl -#include "openvkl/openvkl.h" -// ispc exports -#include "volume/Volume_ispc.h" -#include "volume/VolumetricModel_ispc.h" namespace ospray { @@ -16,7 +11,6 @@ VolumetricModel::VolumetricModel(Volume *_volume) { managedObjectType = OSP_VOLUMETRIC_MODEL; volumeAPI = _volume; - this->ispcEquivalent = ispc::VolumetricModel_create(); } VolumetricModel::~VolumetricModel() @@ -69,20 +63,20 @@ void VolumetricModel::commit() vklRelease(valueRangeData); vklCommit(vklIntervalContext); - ispc::VolumetricModel_set_intervalContext( - ispcEquivalent, vklIntervalContext); + // Pass interval contex to ISPC + getSh()->vklIntervalContext = vklIntervalContext; } - // Finish getting/setting other appearance information // + // Finish getting/setting other appearance information volumeBounds = volume->bounds; - ispc::VolumetricModel_set(ispcEquivalent, - getVolume()->getIE(), - transferFunction->getIE(), - (const ispc::box3f &)volumeBounds, - getParam("densityScale", 1.f), - getParam("anisotropy", 0.f), - getParam("gradientShadingScale", 0.f)); + // Initialize shared structure + getSh()->volume = getVolume()->getSh(); + getSh()->transferFunction = transferFunction->getSh(); + getSh()->boundingBox = volumeBounds; + getSh()->densityScale = getParam("densityScale", 1.f); + getSh()->anisotropy = getParam("anisotropy", 0.f); + getSh()->gradientShadingScale = getParam("gradientShadingScale", 0.f); } RTCGeometry VolumetricModel::embreeGeometryHandle() const @@ -100,11 +94,6 @@ Ref VolumetricModel::getVolume() const return volume; } -void VolumetricModel::setGeomID(int geomID) -{ - ispc::Volume_set_geomID(volume->getIE(), geomID); -} - OSPTYPEFOR_DEFINITION(VolumetricModel *); } // namespace ospray diff --git a/modules/cpu/volume/VolumetricModel.h b/modules/cpu/volume/VolumetricModel.h index 722f10eefd..36a147f182 100644 --- a/modules/cpu/volume/VolumetricModel.h +++ b/modules/cpu/volume/VolumetricModel.h @@ -4,13 +4,15 @@ #pragma once #include "Volume.h" -#include "common/Data.h" - +#include "common/StructShared.h" #include "openvkl/openvkl.h" +#include "volume/VolumetricModelShared.h" + namespace ospray { -struct OSPRAY_SDK_INTERFACE VolumetricModel : public ManagedObject +struct OSPRAY_SDK_INTERFACE VolumetricModel + : public AddStructShared { VolumetricModel(Volume *geometry); ~VolumetricModel() override; @@ -24,7 +26,10 @@ struct OSPRAY_SDK_INTERFACE VolumetricModel : public ManagedObject Ref getVolume() const; - void setGeomID(int geomID); + void setGeomID(int geomID) + { + volume->setGeomID(geomID); + } private: box3f volumeBounds; diff --git a/modules/cpu/volume/VolumetricModel.ih b/modules/cpu/volume/VolumetricModel.ih index f4253bff85..15f3f6f8a7 100644 --- a/modules/cpu/volume/VolumetricModel.ih +++ b/modules/cpu/volume/VolumetricModel.ih @@ -10,24 +10,7 @@ #include "../common/DifferentialGeometry.ih" #include "../common/Ray.ih" -struct VolumetricModel -{ - Volume *volume; //!< instanced Volume - - //! Color and opacity transfer function. - TransferFunction *transferFunction; - - //! interval iterator context - VKLIntervalIteratorContext vklIntervalContext; - - box3f boundingBox; - - //! Volume parameters understood by the pathtracer - float densityScale; - float anisotropy; // the anisotropy of the volume's phase function - // (Heyney-Greenstein) - float gradientShadingScale; -}; +#include "VolumetricModelShared.h" inline void VolumetricModel_postIntersect(const VolumetricModel *uniform self, varying DifferentialGeometry &dg, diff --git a/modules/cpu/volume/VolumetricModel.ispc b/modules/cpu/volume/VolumetricModel.ispc deleted file mode 100644 index 9deb6ca66c..0000000000 --- a/modules/cpu/volume/VolumetricModel.ispc +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2009-2021 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 - -// ospray -#include "VolumetricModel.ih" -#include "common/Ray.ih" -#include "rkcommon/math/box.ih" -#include "rkcommon/math/vec.ih" - -export void *uniform VolumetricModel_create() -{ - VolumetricModel *uniform self = uniform new VolumetricModel; - return self; -} - -export void VolumetricModel_set(void *uniform _self, - void *uniform volume, - void *uniform transferFunction, - const uniform box3f &bounds, - uniform float densityScale, - uniform float anisotropy, - uniform float gradientShadingScale) -{ - VolumetricModel *uniform self = (VolumetricModel * uniform) _self; - - self->transferFunction = (TransferFunction * uniform) transferFunction; - self->boundingBox = bounds; - self->densityScale = densityScale; - self->anisotropy = anisotropy; - self->gradientShadingScale = gradientShadingScale; - self->volume = (Volume * uniform) volume; -} - -export void VolumetricModel_set_intervalContext( - void *uniform _self, void *uniform intervalContext) -{ - VolumetricModel *uniform self = (VolumetricModel * uniform) _self; - self->vklIntervalContext = (VKLIntervalIteratorContext)intervalContext; -} diff --git a/modules/cpu/volume/VolumetricModelShared.h b/modules/cpu/volume/VolumetricModelShared.h new file mode 100644 index 0000000000..aec99d6543 --- /dev/null +++ b/modules/cpu/volume/VolumetricModelShared.h @@ -0,0 +1,36 @@ +// Copyright 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#ifdef __cplusplus +using namespace rkcommon::math; +namespace ispc { +#endif // __cplusplus + +struct Volume; +struct TransferFunction; + +struct VolumetricModel +{ + // instanced Volume + Volume *volume; + + // Color and opacity transfer function. + TransferFunction *transferFunction; + + // interval iterator context + VKLIntervalIteratorContext vklIntervalContext; + + box3f boundingBox; + + // Volume parameters understood by the pathtracer + float densityScale; + float anisotropy; // the anisotropy of the volume's phase function + // (Heyney-Greenstein) + float gradientShadingScale; +}; + +#ifdef __cplusplus +} // namespace ispc +#endif // __cplusplus diff --git a/modules/cpu/volume/transferFunction/LinearTransferFunction.cpp b/modules/cpu/volume/transferFunction/LinearTransferFunction.cpp index c44660eaa9..1837188cb3 100644 --- a/modules/cpu/volume/transferFunction/LinearTransferFunction.cpp +++ b/modules/cpu/volume/transferFunction/LinearTransferFunction.cpp @@ -1,4 +1,4 @@ -// Copyright 2009-2020 Intel Corporation +// Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "LinearTransferFunction.h" @@ -8,7 +8,10 @@ namespace ospray { LinearTransferFunction::LinearTransferFunction() { - ispcEquivalent = ispc::LinearTransferFunction_create(); + getSh()->super.get = ispc::LinearTransferFunction_get_addr(); + getSh()->super.getMaxOpacity = + ispc::LinearTransferFunction_getMaxOpacity_addr(); + getSh()->super.valueRange = range1f(0.0f, 1.0f); } void LinearTransferFunction::commit() @@ -18,8 +21,10 @@ void LinearTransferFunction::commit() colorValues = getParamDataT("color", true); opacityValues = getParamDataT("opacity", true); - ispc::LinearTransferFunction_set( - ispcEquivalent, ispc(colorValues), ispc(opacityValues)); + getSh()->color = *ispc(colorValues); + getSh()->opacity = *ispc(opacityValues); + + precomputeMaxOpacityRanges(); } std::string LinearTransferFunction::toString() const @@ -97,4 +102,51 @@ std::vector LinearTransferFunction::getPositiveOpacityValueRanges() return valueRanges; } +void LinearTransferFunction::precomputeMaxOpacityRanges() +{ + const DataT &opacities = *opacityValues; + const int maxOpacityDim = opacities.size() - 1; + const int maxPrecomputedDim = PRECOMPUTED_OPACITY_SUBRANGE_COUNT - 1; + + // compute the diagonal + for (int i = 0; i < PRECOMPUTED_OPACITY_SUBRANGE_COUNT; i++) { + // figure out the range of array indices we are going to compare; this is a + // conservative range of feasible indices that may be used to lookup + // opacities for any data value within the value range corresponding to [i, + // i]. + const int checkRangeLow = + floor(maxOpacityDim * (float)i / maxPrecomputedDim); + const int checkRangeHigh = + ceil(maxOpacityDim * (float)i / maxPrecomputedDim); + + float maxOpacity = opacities[checkRangeLow]; + for (int opacityIDX = checkRangeLow; opacityIDX <= checkRangeHigh; + opacityIDX++) + maxOpacity = std::max(maxOpacity, opacities[opacityIDX]); + + getSh()->maxOpacityInRange[i][i] = maxOpacity; + } + + // fill out each column from the diagonal up + for (int i = 0; i < PRECOMPUTED_OPACITY_SUBRANGE_COUNT; i++) { + for (int j = i + 1; j < PRECOMPUTED_OPACITY_SUBRANGE_COUNT; j++) { + // figure out the range of array indices we are going to compare; this is + // a conservative range of feasible indices that may be used to lookup + // opacities for any data value within the value range corresponding to + // [i, j]. + const int checkRangeLow = + floor(maxOpacityDim * (float)i / maxPrecomputedDim); + const int checkRangeHigh = + ceil(maxOpacityDim * (float)j / maxPrecomputedDim); + + float maxOpacity = getSh()->maxOpacityInRange[i][i]; + for (int opacityIDX = checkRangeLow; opacityIDX <= checkRangeHigh; + opacityIDX++) + maxOpacity = std::max(maxOpacity, opacities[opacityIDX]); + + getSh()->maxOpacityInRange[i][j] = maxOpacity; + } + } +} + } // namespace ospray diff --git a/modules/cpu/volume/transferFunction/LinearTransferFunction.h b/modules/cpu/volume/transferFunction/LinearTransferFunction.h index 09911d375d..b72f5b246e 100644 --- a/modules/cpu/volume/transferFunction/LinearTransferFunction.h +++ b/modules/cpu/volume/transferFunction/LinearTransferFunction.h @@ -1,4 +1,4 @@ -// Copyright 2009-2019 Intel Corporation +// Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once @@ -7,13 +7,15 @@ #include "TransferFunction.h" #include "common/Data.h" +#include "LinearTransferFunctionShared.h" + namespace ospray { // piecewise linear transfer function -struct OSPRAY_SDK_INTERFACE LinearTransferFunction : public TransferFunction +struct OSPRAY_SDK_INTERFACE LinearTransferFunction + : public AddStructShared { LinearTransferFunction(); - virtual ~LinearTransferFunction() override = default; virtual void commit() override; @@ -25,6 +27,8 @@ struct OSPRAY_SDK_INTERFACE LinearTransferFunction : public TransferFunction private: Ref> colorValues; Ref> opacityValues; + + void precomputeMaxOpacityRanges(); }; } // namespace ospray diff --git a/modules/cpu/volume/transferFunction/LinearTransferFunction.ih b/modules/cpu/volume/transferFunction/LinearTransferFunction.ih index 321ded9e34..0ae05345bc 100644 --- a/modules/cpu/volume/transferFunction/LinearTransferFunction.ih +++ b/modules/cpu/volume/transferFunction/LinearTransferFunction.ih @@ -1,4 +1,4 @@ -// Copyright 2009-2019 Intel Corporation +// Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once @@ -6,15 +6,4 @@ #include "TransferFunction.ih" #include "common/Data.ih" -#define PRECOMPUTED_OPACITY_SUBRANGE_COUNT 32 - -struct LinearTransferFunction -{ - TransferFunction super; - Data1D color; - Data1D opacity; - - // precomputed maximum opacity values per range - float maxOpacityInRange[PRECOMPUTED_OPACITY_SUBRANGE_COUNT] - [PRECOMPUTED_OPACITY_SUBRANGE_COUNT]; -}; +#include "LinearTransferFunctionShared.h" diff --git a/modules/cpu/volume/transferFunction/LinearTransferFunction.ispc b/modules/cpu/volume/transferFunction/LinearTransferFunction.ispc index 440a1250d9..8f5ad331b1 100644 --- a/modules/cpu/volume/transferFunction/LinearTransferFunction.ispc +++ b/modules/cpu/volume/transferFunction/LinearTransferFunction.ispc @@ -1,9 +1,9 @@ -// Copyright 2009-2019 Intel Corporation +// Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "LinearTransferFunction.ih" -inline vec4f LinearTransferFunction_get(const void *uniform _self, float value) +vec4f LinearTransferFunction_get(const void *uniform _self, float value) { const LinearTransferFunction *uniform self = (const LinearTransferFunction *uniform)_self; @@ -36,7 +36,7 @@ inline vec4f LinearTransferFunction_get(const void *uniform _self, float value) return make_vec4f(col, opacity); } -inline float LinearTransferFunction_getMaxOpacity( +float LinearTransferFunction_getMaxOpacity( const void *uniform _self, const range1f &valueRange) { uniform LinearTransferFunction *uniform self = @@ -53,78 +53,12 @@ inline float LinearTransferFunction_getMaxOpacity( return self->maxOpacityInRange[min(i, maxDim)][min(j, maxDim)]; } -inline void LinearTransferFunction_precomputeMaxOpacityRanges( - void *uniform _self) +export void *uniform LinearTransferFunction_get_addr() { - uniform LinearTransferFunction *uniform self = - (uniform LinearTransferFunction * uniform) _self; - - const uniform int maxOpacityDim = self->opacity.numItems - 1; - const uniform int maxPrecomputedDim = PRECOMPUTED_OPACITY_SUBRANGE_COUNT - 1; - - // compute the diagonal - for (uniform int i = 0; i < PRECOMPUTED_OPACITY_SUBRANGE_COUNT; i++) { - // figure out the range of array indices we are going to compare; this is a - // conservative range of feasible indices that may be used to lookup - // opacities for any data value within the value range corresponding to [i, - // i]. - const uniform int checkRangeLow = - floor(maxOpacityDim * (float)i / maxPrecomputedDim); - const uniform int checkRangeHigh = - ceil(maxOpacityDim * (float)i / maxPrecomputedDim); - - uniform float maxOpacity = get_float(self->opacity, checkRangeLow); - for (uniform int opacityIDX = checkRangeLow; opacityIDX <= checkRangeHigh; - opacityIDX++) - maxOpacity = max(maxOpacity, get_float(self->opacity, opacityIDX)); - - self->maxOpacityInRange[i][i] = maxOpacity; - } - - // fill out each column from the diagonal up - for (uniform int i = 0; i < PRECOMPUTED_OPACITY_SUBRANGE_COUNT; i++) { - for (uniform int j = i + 1; j < PRECOMPUTED_OPACITY_SUBRANGE_COUNT; j++) { - // figure out the range of array indices we are going to compare; this is - // a conservative range of feasible indices that may be used to lookup - // opacities for any data value within the value range corresponding to - // [i, j]. - const uniform int checkRangeLow = - floor(maxOpacityDim * (float)i / maxPrecomputedDim); - const uniform int checkRangeHigh = - ceil(maxOpacityDim * (float)j / maxPrecomputedDim); - - uniform float maxOpacity = self->maxOpacityInRange[i][i]; - for (uniform int opacityIDX = checkRangeLow; opacityIDX <= checkRangeHigh; - opacityIDX++) - maxOpacity = max(maxOpacity, get_float(self->opacity, opacityIDX)); - - self->maxOpacityInRange[i][j] = maxOpacity; - } - } -} - -export void *uniform LinearTransferFunction_create() -{ - LinearTransferFunction *uniform self = uniform new LinearTransferFunction; - - TransferFunction_Constructor(&self->super, - LinearTransferFunction_get, - LinearTransferFunction_getMaxOpacity); - Data1D_Constructor(&self->color); - Data1D_Constructor(&self->opacity); - - return self; + return LinearTransferFunction_get; } -export void LinearTransferFunction_set(void *uniform _self, - const Data1D *uniform color, - const Data1D *uniform opacity) +export void *uniform LinearTransferFunction_getMaxOpacity_addr() { - LinearTransferFunction *uniform self = - (LinearTransferFunction * uniform) _self; - - self->color = *color; - self->opacity = *opacity; - - LinearTransferFunction_precomputeMaxOpacityRanges(_self); + return LinearTransferFunction_getMaxOpacity; } diff --git a/modules/cpu/volume/transferFunction/LinearTransferFunctionShared.h b/modules/cpu/volume/transferFunction/LinearTransferFunctionShared.h new file mode 100644 index 0000000000..e839dbb8b6 --- /dev/null +++ b/modules/cpu/volume/transferFunction/LinearTransferFunctionShared.h @@ -0,0 +1,28 @@ +// Copyright 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "TransferFunctionShared.h" + +#define PRECOMPUTED_OPACITY_SUBRANGE_COUNT 32 + +#ifdef __cplusplus +namespace ispc { +#endif // __cplusplus + +struct LinearTransferFunction +{ + TransferFunction super; + + Data1D color; + Data1D opacity; + + // precomputed maximum opacity values per range + float maxOpacityInRange[PRECOMPUTED_OPACITY_SUBRANGE_COUNT] + [PRECOMPUTED_OPACITY_SUBRANGE_COUNT]; +}; + +#ifdef __cplusplus +} // namespace ispc +#endif // __cplusplus \ No newline at end of file diff --git a/modules/cpu/volume/transferFunction/TransferFunction.cpp b/modules/cpu/volume/transferFunction/TransferFunction.cpp index 772b62a9e5..761c54babf 100644 --- a/modules/cpu/volume/transferFunction/TransferFunction.cpp +++ b/modules/cpu/volume/transferFunction/TransferFunction.cpp @@ -1,10 +1,9 @@ -// Copyright 2009-2020 Intel Corporation +// Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // ospray #include "TransferFunction.h" #include "common/Util.h" -#include "volume/transferFunction/TransferFunction_ispc.h" namespace ospray { @@ -21,7 +20,7 @@ void TransferFunction::commit() { auto param = getParam("valueRange", vec2f(0.0f, 1.0f)); valueRange = range1f(param.x, param.y); - ispc::TransferFunction_set(ispcEquivalent, (const ispc::box1f &)(valueRange)); + getSh()->valueRange = valueRange; } std::string TransferFunction::toString() const diff --git a/modules/cpu/volume/transferFunction/TransferFunction.h b/modules/cpu/volume/transferFunction/TransferFunction.h index 5257407fab..04a098256d 100644 --- a/modules/cpu/volume/transferFunction/TransferFunction.h +++ b/modules/cpu/volume/transferFunction/TransferFunction.h @@ -1,17 +1,20 @@ -// Copyright 2009-2020 Intel Corporation +// Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once #include "common/Managed.h" +#include "common/StructShared.h" #include "common/Util.h" +#include "TransferFunctionShared.h" + namespace ospray { -struct OSPRAY_SDK_INTERFACE TransferFunction : public ManagedObject +struct OSPRAY_SDK_INTERFACE TransferFunction + : public AddStructShared { TransferFunction(); - virtual ~TransferFunction() override = default; virtual void commit() override; virtual std::string toString() const override; diff --git a/modules/cpu/volume/transferFunction/TransferFunction.ih b/modules/cpu/volume/transferFunction/TransferFunction.ih index 7121e09156..865722fc53 100644 --- a/modules/cpu/volume/transferFunction/TransferFunction.ih +++ b/modules/cpu/volume/transferFunction/TransferFunction.ih @@ -1,4 +1,4 @@ -// Copyright 2009-2020 Intel Corporation +// Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once @@ -7,24 +7,4 @@ #include "rkcommon/math/box.ih" #include "rkcommon/math/vec.ih" -typedef vec4f (*TransferFunction_getFct)(const void *uniform self, float value); - -typedef float (*TransferFunction_getMaxOpacityFct)( - const void *uniform self, const range1f &valueRange); - -struct TransferFunction -{ - range1f valueRange; - - TransferFunction_getFct get; - TransferFunction_getMaxOpacityFct getMaxOpacity; -}; - -inline void TransferFunction_Constructor(TransferFunction *uniform self, - uniform TransferFunction_getFct get, - uniform TransferFunction_getMaxOpacityFct getMaxOpacity) -{ - self->valueRange = make_box1f(0.0f, 1.0f); - self->get = get; - self->getMaxOpacity = getMaxOpacity; -} +#include "TransferFunctionShared.h" \ No newline at end of file diff --git a/modules/cpu/volume/transferFunction/TransferFunction.ispc b/modules/cpu/volume/transferFunction/TransferFunction.ispc deleted file mode 100644 index 0d256cd685..0000000000 --- a/modules/cpu/volume/transferFunction/TransferFunction.ispc +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2009-2019 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 - -#include "TransferFunction.ih" - -export void TransferFunction_set( - void *uniform _self, const uniform range1f &range) -{ - TransferFunction *uniform self = (TransferFunction * uniform) _self; - - self->valueRange = range; -} diff --git a/modules/cpu/volume/transferFunction/TransferFunctionShared.h b/modules/cpu/volume/transferFunction/TransferFunctionShared.h new file mode 100644 index 0000000000..b45be2b232 --- /dev/null +++ b/modules/cpu/volume/transferFunction/TransferFunctionShared.h @@ -0,0 +1,29 @@ +// Copyright 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#ifdef __cplusplus +using namespace rkcommon::math; +namespace ispc { +typedef void *TransferFunction_getFct; +typedef void *TransferFunction_getMaxOpacityFct; +#else +struct TransferFunction; +typedef vec4f (*TransferFunction_getFct)( + const TransferFunction *uniform self, float value); +typedef float (*TransferFunction_getMaxOpacityFct)( + const TransferFunction *uniform self, const range1f &valueRange); +#endif // __cplusplus + +struct TransferFunction +{ + range1f valueRange; + + TransferFunction_getFct get; + TransferFunction_getMaxOpacityFct getMaxOpacity; +}; + +#ifdef __cplusplus +} // namespace ispc +#endif // __cplusplus From e18f3d15bd0eebdac55903562ff1784e782f6a1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20G=C3=BCnther?= Date: Fri, 10 Dec 2021 16:40:46 +0100 Subject: [PATCH 07/26] static_assert --- modules/cpu/common/StructShared.h | 46 +++++++++++++++++++++- modules/cpu/volume/Volume.cpp | 2 +- modules/cpu/volume/VolumetricModelShared.h | 6 --- 3 files changed, 45 insertions(+), 9 deletions(-) diff --git a/modules/cpu/common/StructShared.h b/modules/cpu/common/StructShared.h index ad6c2d3e96..7293bb6cd5 100644 --- a/modules/cpu/common/StructShared.h +++ b/modules/cpu/common/StructShared.h @@ -42,6 +42,41 @@ struct StructSharedGet T *getSh() const; }; +// Traits ///////////////////////////////////////////////////// + +template +struct make_void +{ + using type = void; +}; + +template +using void_t = typename make_void::type; + +template +struct get_base_structshared_or +{ + using type = S; +}; + +template +struct get_base_structshared_or> +{ + using type = typename T::StructShared_t; +}; + +template +struct get_super_or +{ + using type = S; +}; + +template +struct get_super_or> +{ + using type = decltype(S::super); +}; + // AddStructShared //////////////////////////////////////////// template @@ -50,8 +85,12 @@ struct AddStructShared public Base, public virtual StructSharedPtr { - // using StructShared_t = Struct; + using StructShared_t = Struct; using StructSharedGet>::getSh; + static_assert( + std::is_same::type, + typename get_super_or::type>::value, + "StructShared_t needs to have 'super' member of type Base::StructShared_t"); AddStructShared() : StructSharedGet>(&structSharedPtr) {} @@ -88,7 +127,10 @@ struct D2 { D1 super; char c; }; struct ShouldPass1 : public AddStructShared {}; struct ShouldPass2 : public AddStructShared {}; struct ShouldPass3 : public AddStructShared {}; -struct ShouldPass4 : public AddStructShared {}; + +//struct ShouldFail1 : public AddStructShared {}; +//struct ShouldFail2 : public AddStructShared {}; +//struct ShouldFail3 : public AddStructShared {}; // clang-format on } // namespace test diff --git a/modules/cpu/volume/Volume.cpp b/modules/cpu/volume/Volume.cpp index 89d584f0e2..c2232ccf3c 100644 --- a/modules/cpu/volume/Volume.cpp +++ b/modules/cpu/volume/Volume.cpp @@ -90,7 +90,7 @@ void Volume::commit() // Initialize shared structure getSh()->vklVolume = vklVolume; getSh()->vklSampler = vklSampler; - getSh()->boundingBox = *(box3f *)&bounds; + getSh()->boundingBox = bounds; } void Volume::checkDataStride(const Data *data) const diff --git a/modules/cpu/volume/VolumetricModelShared.h b/modules/cpu/volume/VolumetricModelShared.h index aec99d6543..52bf91ce9b 100644 --- a/modules/cpu/volume/VolumetricModelShared.h +++ b/modules/cpu/volume/VolumetricModelShared.h @@ -13,15 +13,9 @@ struct TransferFunction; struct VolumetricModel { - // instanced Volume Volume *volume; - - // Color and opacity transfer function. TransferFunction *transferFunction; - - // interval iterator context VKLIntervalIteratorContext vklIntervalContext; - box3f boundingBox; // Volume parameters understood by the pathtracer From 8838b006d6203f4e3d45c16f130cae6f9621ad96 Mon Sep 17 00:00:00 2001 From: Sean McDuffee Date: Mon, 29 Nov 2021 13:40:35 -0500 Subject: [PATCH 08/26] add solid angle sampling to quad lights --- CHANGELOG.md | 2 + modules/cpu/lights/CylinderLight.ispc | 8 +- modules/cpu/lights/QuadLight.ispc | 28 ++- modules/cpu/lights/SphericalQuadSampling.ih | 236 +++++++++----------- 4 files changed, 129 insertions(+), 145 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 268d316e72..4db98bedbf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ Version History - OSPRAY_APPS_EXAMPLES refactored to OSPRAY_ENABLE_APPS_EXAMPLES - OSPRAY_APPS_TUTORIALS refactored to OSPRAY_ENABLE_APPS_TUTORIALS - OSPRAY_APPS_TESTING refactored to OSPRAY_ENABLE_APPS_TESTING +- Changed from area sampling to solid angle sampling for quad lights + in the pathtracer ### Changes in v2.8.0: diff --git a/modules/cpu/lights/CylinderLight.ispc b/modules/cpu/lights/CylinderLight.ispc index 1e43a63c98..bbe38247f5 100644 --- a/modules/cpu/lights/CylinderLight.ispc +++ b/modules/cpu/lights/CylinderLight.ispc @@ -102,12 +102,8 @@ inline void cylinderSampleSolidAngle(const DifferentialGeometry &dg, // compute spherical quad bounding cylinder vec3f q0, e0, e1; cylinderComputeRectBounds(position0, position1, radius, dg.P, q0, e0, e1, S); - const float e0l = length(e0); - const float e1l = length(e1); - const vec3f e0n = e0 / e0l; - const vec3f e1n = e1 / e1l; - const vec3f n = normalize(cross(e0n, e1n)); - SphericalQuad quad = SphericalQuad_create(q0, e0n, e0l, e1n, e1l, n, dg.P); + const vec3f n = normalize(cross(e0, e1)); + SphericalQuad quad = SphericalQuad_create(q0, e0, e1, n, dg.P); // warped samples in solid angle space const vec3f cosW = computeCosineWeightedRNG(q0, e0, e1, dg.P, dg.Ng, sp); diff --git a/modules/cpu/lights/QuadLight.ispc b/modules/cpu/lights/QuadLight.ispc index 852290b12e..a13c12873b 100644 --- a/modules/cpu/lights/QuadLight.ispc +++ b/modules/cpu/lights/QuadLight.ispc @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 #include "Light.ih" +#include "SphericalQuadSampling.ih" #include "common/Instance.ih" struct QuadLight @@ -37,12 +38,16 @@ inline Light_SampleRes Sample(const QuadLight *uniform self, s.x = sp.x > 0.5f ? sp.x - 0.5f : sp.x + 0.5f; s.y = sp.y > 0.5f ? sp.y - 0.5f : sp.y + 0.5f; - // sample position on light with density ppdf = 1/area - // TODO: use solid angle sampling - const vec3f p = pos + edge1 * s.x + edge2 * s.y; + // create spherical quad for solid angle sampling + SphericalQuad quad = + SphericalQuad_create(pos, edge1, edge2, neg(nnormal), dg.P); - // extant light vector from the hit point - const vec3f dir = p - dg.P; + // bilinear warped cosine weight approximation + const vec3f cosW = + computeCosineWeightedRNG(pos, edge1, edge2, dg.P, dg.Ng, s); + + // sample quad + const vec3f dir = sampleSphericalQuad(quad, make_vec2f(cosW.x, cosW.y)); const float dist = length(dir); // normalized light vector @@ -51,10 +56,11 @@ inline Light_SampleRes Sample(const QuadLight *uniform self, // convert to pdf wrt. solid angle const float cosd = dot(nnormal, res.dir); - res.pdf = ppdf * sqr(dist) / abs(cosd); + res.pdf = quad.S == 0.f ? 0.f : cosW.z / quad.S; // emit only to one side - res.weight = cosd > 0.f ? self->radiance * rcp(res.pdf) : make_vec3f(0.f); + res.weight = (cosd > 0.f) && (res.pdf != 0.f) ? self->radiance * rcp(res.pdf) + : make_vec3f(0.f); return res; } @@ -141,8 +147,12 @@ inline Light_EvalRes Eval(const QuadLight *uniform self, if (dist <= minDist | dist > maxDist) return res; - res.radiance = self->radiance; - res.pdf = ppdf * sqr(dist) * rcosd; + SphericalQuad quad = + SphericalQuad_create(pos, edge1, edge2, neg(nnormal), dg.P); + if (quad.S > 0.f) { + res.radiance = self->radiance; + res.pdf = rcp(quad.S); + } return res; } diff --git a/modules/cpu/lights/SphericalQuadSampling.ih b/modules/cpu/lights/SphericalQuadSampling.ih index 2fe14d0df7..601f529d78 100644 --- a/modules/cpu/lights/SphericalQuadSampling.ih +++ b/modules/cpu/lights/SphericalQuadSampling.ih @@ -11,72 +11,77 @@ struct SphericalQuad float S, k, n0z, n0z2, n2z, x0, x1, y0, y02, y1, y12, z0, z02; }; -SphericalQuad SphericalQuad_create(const vec3f &q0, - const vec3f &e0, - const float e0l, - const vec3f &e1, - const float e1l, - const vec3f &n, - const vec3f &P) -{ - SphericalQuad quad; - quad.e0 = e0; - quad.e1 = e1; - quad.z = n; - - // compute rectangle coords in local reference system - vec3f d = q0 - P; - quad.z0 = dot(d, quad.z); - - // flip z to make it point against Q - quad.z = quad.z0 > 0.f ? neg(quad.z) : quad.z; - quad.z0 = -abs(quad.z0); - - quad.z02 = sqr(quad.z0); - - quad.x0 = dot(d, quad.e0); - quad.y0 = dot(d, quad.e1); - quad.y02 = sqr(quad.y0); - quad.x1 = quad.x0 + e0l; - quad.y1 = quad.y0 + e1l; - quad.y12 = sqr(quad.y1) * quad.y1; - - // create vectors to four vertices - vec3f v00 = make_vec3f(quad.x0, quad.y0, quad.z0); - vec3f v01 = make_vec3f(quad.x0, quad.y1, quad.z0); - vec3f v10 = make_vec3f(quad.x1, quad.y0, quad.z0); - vec3f v11 = make_vec3f(quad.x1, quad.y1, quad.z0); - - // compute normals to edges - vec3f n0 = normalize(cross(v00, v10)); - quad.n0z = n0.z; - quad.n0z2 = n0.z * n0.z; - vec3f n1 = normalize(cross(v10, v11)); - vec3f n2 = normalize(cross(v11, v01)); - quad.n2z = n2.z; - vec3f n3 = normalize(cross(v01, v00)); - - // compute internal angles (gamma_i) - float g0 = acos(clamp(-dot(n0, n1), -1.f, 1.f)); - float g1 = acos(clamp(-dot(n1, n2), -1.f, 1.f)); - float g2 = acos(clamp(-dot(n2, n3), -1.f, 1.f)); - float g3 = acos(clamp(-dot(n3, n0), -1.f, 1.f)); - - // compute predefined constants - quad.k = (float)two_pi - g2 - g3; - - // compute solid angle from internal angles - quad.S = max(0.f, g0 + g1 - quad.k); - - return quad; -} +#define template_SphericalQuad_create(univary) \ + inline SphericalQuad SphericalQuad_create(const univary vec3f &q0, \ + const univary vec3f &e0, \ + const univary vec3f &e1, \ + const univary vec3f &n, \ + const vec3f &P) \ + { \ + const univary float e0l = length(e0); \ + const univary float e1l = length(e1); \ + SphericalQuad quad; \ + quad.e0 = e0 / e0l; \ + quad.e1 = e1 / e1l; \ + quad.z = n; \ + \ + /* compute rectangle coords in local reference system */ \ + vec3f d = q0 - P; \ + quad.z0 = dot(d, quad.z); \ + \ + /* flip z to make it point against Q */ \ + quad.z = quad.z0 > 0.f ? neg(quad.z) : quad.z; \ + quad.z0 = -abs(quad.z0); \ + \ + quad.z02 = sqr(quad.z0); \ + \ + quad.x0 = dot(d, quad.e0); \ + quad.y0 = dot(d, quad.e1); \ + quad.y02 = sqr(quad.y0); \ + quad.x1 = quad.x0 + e0l; \ + quad.y1 = quad.y0 + e1l; \ + quad.y12 = sqr(quad.y1) * quad.y1; \ + \ + /* create vectors to four vertices */ \ + vec3f v00 = make_vec3f(quad.x0, quad.y0, quad.z0); \ + vec3f v01 = make_vec3f(quad.x0, quad.y1, quad.z0); \ + vec3f v10 = make_vec3f(quad.x1, quad.y0, quad.z0); \ + vec3f v11 = make_vec3f(quad.x1, quad.y1, quad.z0); \ + \ + /* compute normals to edges */ \ + vec3f n0 = normalize(cross(v00, v10)); \ + quad.n0z = n0.z; \ + quad.n0z2 = n0.z * n0.z; \ + vec3f n1 = normalize(cross(v10, v11)); \ + vec3f n2 = normalize(cross(v11, v01)); \ + quad.n2z = n2.z; \ + vec3f n3 = normalize(cross(v01, v00)); \ + \ + /* compute internal angles (gamma_i) */ \ + float g0 = acos(clamp(-dot(n0, n1), -1.f, 1.f)); \ + float g1 = acos(clamp(-dot(n1, n2), -1.f, 1.f)); \ + float g2 = acos(clamp(-dot(n2, n3), -1.f, 1.f)); \ + float g3 = acos(clamp(-dot(n3, n0), -1.f, 1.f)); \ + \ + /* compute predefined constants */ \ + quad.k = (float)two_pi - g2 - g3; \ + \ + /* compute solid angle from internal angles */ \ + quad.S = max(0.f, g0 + g1 - quad.k); \ + \ + return quad; \ + } + +template_SphericalQuad_create(varying); +template_SphericalQuad_create(uniform); +#undef template_SphericalQuad_create inline float sign(float x) { return ((float)(0.f < x) - (float)(x < 0.f)); } -vec3f sampleSphericalQuad(const SphericalQuad &quad, const vec2f &s) +inline vec3f sampleSphericalQuad(const SphericalQuad &quad, const vec2f &s) { // 1. compute cu const float au = s.x * quad.S + quad.k; @@ -102,7 +107,7 @@ vec3f sampleSphericalQuad(const SphericalQuad &quad, const vec2f &s) return (xu * quad.e0 + yv * quad.e1 + quad.z0 * quad.z); } -float solve_quadratic(float A, float B, float C) +inline float solve_quadratic(float A, float B, float C) { // see https://people.csail.mit.edu/bkph/articles/Quadratics.pdf if (A == 0.f) @@ -149,74 +154,45 @@ inline float fv(float r, float u, float W00, float W01, float W10, float W11) return solve_quadratic(A, B, C); } -vec3f computeCosineWeightedRNG(const vec3f &q0, - const vec3f &e0, - const vec3f e1, - const vec3f &P, - const vec3f &Ng, - const vec2f &s) -{ - // code from - // https://casual-effects.com/research/Hart2020Sampling/Hart2020Sampling.pdf - vec3f wpn[4], wp[4]; - float prob[4]; - wp[0] = q0; - wp[1] = q0 + e0; - wp[2] = q0 + e1; - wp[3] = wp[1] + e1; - for (int i = 0; i < 4; i++) { - wpn[i] = normalize(wp[i] - P); - // cosine term of ray with illuminated surface - prob[i] = max(0.f, dot(wpn[i], Ng)); - } - - if ((prob[0] + prob[2]) == 0.f) { - return make_vec3f(s.x, s.y, 1.f); +#define template_computeCosineWeightedRNG(univary) \ + inline vec3f computeCosineWeightedRNG(const univary vec3f &q0, \ + const univary vec3f &e0, \ + const univary vec3f &e1, \ + const vec3f &P, \ + const vec3f &Ng, \ + const vec2f &s) \ + { \ + /*code from \ + https://casual-effects.com/research/Hart2020Sampling/Hart2020Sampling.pdf*/ \ + vec3f wpn[4]; \ + univary vec3f wp[4]; \ + float prob[4]; \ + wp[0] = q0; \ + wp[1] = q0 + e0; \ + wp[2] = q0 + e1; \ + wp[3] = wp[1] + e1; \ + for (int i = 0; i < 4; i++) { \ + wpn[i] = normalize(wp[i] - P); \ + /* cosine term of ray with illuminated surface */ \ + prob[i] = max(0.f, dot(wpn[i], Ng)); \ + } \ + \ + if ((prob[0] + prob[2]) == 0.f) { \ + return make_vec3f(s.x, s.y, 1.f); \ + } \ + \ + const float totProb = prob[0] + prob[2] + prob[1] + prob[3]; \ + const float u = fu(s.x, prob[0], prob[2], prob[1], prob[3]); \ + const float v = fv(s.y, u, prob[0], prob[2], prob[1], prob[3]); \ + \ + const float pdf = 4.f \ + * ((1.f - u) * (1.f - v) * prob[0] + u * (1.f - v) * prob[1] \ + + u * v * prob[3] + (1.f - u) * v * prob[2]) \ + / totProb; \ + \ + return make_vec3f(u, v, pdf); \ } - const float totProb = prob[0] + prob[2] + prob[1] + prob[3]; - const float u = fu(s.x, prob[0], prob[2], prob[1], prob[3]); - const float v = fv(s.y, u, prob[0], prob[2], prob[1], prob[3]); - - const float pdf = 4.f - * ((1.f - u) * (1.f - v) * prob[0] + u * (1.f - v) * prob[1] - + u * v * prob[3] + (1.f - u) * v * prob[2]) - / totProb; - - return make_vec3f(u, v, pdf); -} - -float computeCosineWeightedPDF(const vec3f &q0, - const vec3f &e0, - const vec3f e1, - const vec3f &P, - const vec3f &Ng, - const vec2f &uv) -{ - // code from - // https://casual-effects.com/research/Hart2020Sampling/Hart2020Sampling.pdf - vec3f wpn[4], wp[4]; - float prob[4]; - wp[0] = q0; - wp[1] = q0 + e0; - wp[2] = q0 + e1; - wp[3] = wp[1] + e1; - for (int i = 0; i < 4; i++) { - wpn[i] = normalize(wp[i] - P); - // cosine term of ray with illuminated surface - prob[i] = max(0.f, dot(wpn[i], Ng)); - } - - if ((prob[0] + prob[2]) == 0.f) { - return 1.f; - } - - const float totProb = prob[0] + prob[2] + prob[1] + prob[3]; - - const float pdf = 4.f - * ((1.f - uv.x) * (1.f - uv.y) * prob[0] + uv.x * (1.f - uv.y) * prob[1] - + uv.x * uv.y * prob[3] + (1.f - uv.x) * uv.y * prob[2]) - / totProb; - - return pdf; -} +template_computeCosineWeightedRNG(varying); +template_computeCosineWeightedRNG(uniform); +#undef template_computeCosineWeightedRNG From 06c40014317325d7da64cdcd37caa398b49f651b Mon Sep 17 00:00:00 2001 From: Sean McDuffee Date: Thu, 9 Dec 2021 14:02:27 -0500 Subject: [PATCH 09/26] switch to regular area sample for scivis --- modules/cpu/lights/QuadLight.ispc | 49 ++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 11 deletions(-) diff --git a/modules/cpu/lights/QuadLight.ispc b/modules/cpu/lights/QuadLight.ispc index a13c12873b..f03eef54e8 100644 --- a/modules/cpu/lights/QuadLight.ispc +++ b/modules/cpu/lights/QuadLight.ispc @@ -21,22 +21,49 @@ struct QuadLight // Implementation ////////////////////////////////////////////////////////////////////////////// +inline Light_SampleRes SampleCenter(const uniform vec3f &pos, + const uniform vec3f &edge1, + const uniform vec3f &edge2, + const uniform float ppdf, + const uniform vec3f &nnormal, + const uniform vec3f &radiance, + const DifferentialGeometry &dg) +{ + Light_SampleRes res; + const vec3f p = pos + edge1 * 0.5f + edge2 * 0.5f; -inline Light_SampleRes Sample(const QuadLight *uniform self, - const uniform vec3f &pos, + // extant light vector from the hit point + const vec3f dir = p - dg.P; + const float dist = length(dir); + + // normalized light vector + res.dir = dir / dist; + res.dist = dist; + + // convert to pdf wrt. solid angle + const float cosd = dot(nnormal, res.dir); + res.pdf = ppdf * sqr(dist) / abs(cosd); + + // emit only to one side + res.weight = cosd > 0.f ? radiance * rcp(res.pdf) : make_vec3f(0.f); + + return res; +} + +inline Light_SampleRes Sample(const uniform vec3f &pos, const uniform vec3f &edge1, const uniform vec3f &edge2, const uniform float ppdf, const uniform vec3f &nnormal, + const uniform vec3f &radiance, const DifferentialGeometry &dg, - const vec2f &sp) + const vec2f &s) { Light_SampleRes res; - // (0, 0) should sample center --> add 0.5 CranleyPatterson rotation - vec2f s; - s.x = sp.x > 0.5f ? sp.x - 0.5f : sp.x + 0.5f; - s.y = sp.y > 0.5f ? sp.y - 0.5f : sp.y + 0.5f; + if (s.x == 0.0f && s.y == 0.0f) { // XXX SciVis + return SampleCenter(pos, edge1, edge2, ppdf, nnormal, radiance, dg); + } // create spherical quad for solid angle sampling SphericalQuad quad = @@ -59,7 +86,7 @@ inline Light_SampleRes Sample(const QuadLight *uniform self, res.pdf = quad.S == 0.f ? 0.f : cosW.z / quad.S; // emit only to one side - res.weight = (cosd > 0.f) && (res.pdf != 0.f) ? self->radiance * rcp(res.pdf) + res.weight = (cosd > 0.f) && (res.pdf != 0.f) ? radiance * rcp(res.pdf) : make_vec3f(0.f); return res; @@ -72,12 +99,12 @@ Light_SampleRes QuadLight_sample(const Light *uniform super, { const QuadLight *uniform self = (QuadLight * uniform) super; assert(self); - return Sample(self, - self->position, + return Sample(self->position, self->edge1, self->edge2, self->ppdf, self->nnormal, + self->radiance, dg, sp); } @@ -108,7 +135,7 @@ Light_SampleRes QuadLight_sample_instanced(const Light *uniform super, const uniform vec3f nnormal = ndirection * ppdf; // normalize // sample light - res = Sample(self, pos, edge1, edge2, ppdf, nnormal, dg, sp); + res = Sample(pos, edge1, edge2, ppdf, nnormal, self->radiance, dg, sp); } return res; } From 67dfbc0318e27fc1e39710561a1ded2328e18762 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20G=C3=BCnther?= Date: Thu, 2 Dec 2021 10:34:47 +0100 Subject: [PATCH 10/26] Cleanup --- doc/api.md | 20 ++++++++++---------- modules/cpu/common/World.cpp | 3 ++- modules/mpi/CMakeLists.txt | 2 +- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/doc/api.md b/doc/api.md index f2bdae46dc..e5c0667de6 100644 --- a/doc/api.md +++ b/doc/api.md @@ -27,7 +27,7 @@ calling OSPRay parses (and removes) its known command line parameters from your application's `main` function. For an example see the [tutorial]. For possible error codes see section [Error Handling and Status Messages]. -It is important to note that the arguments passed to `ospInit()` are +It is important to note that the arguments passed to `ospInit` are processed in order they are listed. The following parameters (which are prefixed by convention with "`--osp:`") are understood: @@ -271,7 +271,7 @@ in order to register a callback function of type which OSPRay will use to emit status messages. By default, OSPRay uses a callback which does nothing, so any output desired by an application will require that a callback is provided. Note that callbacks for C++ -`std::cout` and `std::cerr` can be alternatively set through `ospInit()` +`std::cout` and `std::cerr` can be alternatively set through `ospInit` or the `OSPRAY_LOG_OUTPUT` environment variable. Applications can clear either callback by passing `NULL` instead of an @@ -298,7 +298,7 @@ exit), the OSPRay API should be finalized with This API call ensures that the current device is cleaned up appropriately. Due to static object allocation having non-deterministic -ordering, it is recommended that applications call `ospShutdown()` +ordering, it is recommended that applications call `ospShutdown` before the calling application process terminates. Objects @@ -3079,13 +3079,6 @@ for applications to query exactly how long an asynchronous task executed without the overhead of measuring both task execution + synchronization by the calling application. -### Asynchronously Rendering and ospCommit() - -The use of either `ospRenderFrame` or `ospRenderFrame` requires -that all objects in the scene being rendered have been committed before -rendering occurs. If a call to `ospCommit()` happens while a frame is -rendered, the result is undefined behavior and should be avoided. - ### Synchronous Rendering For convenience in certain use cases, `ospray_util.h` provides a @@ -3101,6 +3094,13 @@ This version is the equivalent of: This version is closest to `ospRenderFrame` from OSPRay v1.x. +### Rendering and ospCommit + +The use of either `ospRenderFrame` or `ospRenderFrameBlocking` requires +that all objects in the scene being rendered have been committed before +rendering occurs. If a call to `ospCommit` happens while a frame is +rendered, the result is undefined behavior and should be avoided. + Distributed Rendering with MPI ============================== diff --git a/modules/cpu/common/World.cpp b/modules/cpu/common/World.cpp index aee42a4548..e98cc1520d 100644 --- a/modules/cpu/common/World.cpp +++ b/modules/cpu/common/World.cpp @@ -88,7 +88,8 @@ void World::commit() postStatusMsg(OSP_LOG_DEBUG) << "=======================================================\n" - << "Committing world, which has " << numInstances << " instances"; + << "Committing world, which has " << numInstances << " instances and " + << (lights ? lights->size() : 0) << " lights"; geometriesInstIEs.clear(); volumesInstIEs.clear(); diff --git a/modules/mpi/CMakeLists.txt b/modules/mpi/CMakeLists.txt index e8991fbb5c..02a2ae70f4 100644 --- a/modules/mpi/CMakeLists.txt +++ b/modules/mpi/CMakeLists.txt @@ -3,7 +3,7 @@ cmake_dependent_option( OSPRAY_MODULE_MPI - "Build the MPI module (Requires ISPC module)" + "Build the MPI module" OFF OSPRAY_MODULE_CPU OFF From 250c95a251bc31d6e9ac8071f547e61e70061694 Mon Sep 17 00:00:00 2001 From: Trevor Thomson Date: Thu, 16 Dec 2021 13:15:21 -0500 Subject: [PATCH 11/26] Avoid unknown option for macos linker --- cmake/compiler/icc.cmake | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cmake/compiler/icc.cmake b/cmake/compiler/icc.cmake index 00e3443eb8..da6c5ca25a 100644 --- a/cmake/compiler/icc.cmake +++ b/cmake/compiler/icc.cmake @@ -13,8 +13,10 @@ if (APPLE) set(CMAKE_SHARED_LINKER_FLAGS "-dynamiclib ${CMAKE_SHARED_LINKER_FLAGS_INIT}") set(CMAKE_CXX_FLAGS "-mmacosx-version-min=10.8 ${CMAKE_CXX_FLAGS}") # we only use MacOSX 10.8 features set(CMAKE_CXX_FLAGS "-stdlib=libc++ ${CMAKE_CXX_FLAGS}") # link against C++11 stdlib +else() + # avoid exporting ICC specific symbols from OSPRay + set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--exclude-libs=ALL ${CMAKE_SHARED_LINKER_FLAGS}") endif() -# enable -static-intel and avoid to export ICC specific symbols from OSPRay +# enable -static-intel set(CMAKE_CXX_FLAGS "-static-intel ${CMAKE_CXX_FLAGS}") -set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--exclude-libs=ALL ${CMAKE_SHARED_LINKER_FLAGS}") From 1e9400a75a23eedc6e93c04d8c4f780424c1132c Mon Sep 17 00:00:00 2001 From: Will Usher Date: Fri, 17 Dec 2021 10:08:26 -0800 Subject: [PATCH 12/26] Change TileError to use rkcommon AlignedVector instead of aligned_malloc This fixes a bug where the TileError class was not copyable but didn't delete the copy ctor and copy-assign operators, allowing for a double free when copying them. --- modules/cpu/fb/TileError.cpp | 16 ++++------------ modules/cpu/fb/TileError.h | 11 ++++++++--- modules/mpi/ospray/fb/DistributedFrameBuffer.cpp | 2 +- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/modules/cpu/fb/TileError.cpp b/modules/cpu/fb/TileError.cpp index 984bb7476f..17a620dbdf 100644 --- a/modules/cpu/fb/TileError.cpp +++ b/modules/cpu/fb/TileError.cpp @@ -1,4 +1,4 @@ -// Copyright 2009-2019 Intel Corporation +// Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "TileError.h" @@ -7,23 +7,15 @@ namespace ospray { TileError::TileError(const vec2i &_numTiles) - : numTiles(_numTiles), tiles(_numTiles.x * _numTiles.y) + : numTiles(_numTiles), + tiles(_numTiles.x * _numTiles.y), + tileErrorBuffer(tiles) { - if (tiles > 0) - tileErrorBuffer = alignedMalloc(tiles); - else - tileErrorBuffer = nullptr; - // maximum number of regions: all regions are of size 3 are split in half errorRegion.reserve(divRoundUp(tiles * 2, 3)); clear(); } -TileError::~TileError() -{ - alignedFree(tileErrorBuffer); -} - void TileError::clear() { for (int i = 0; i < tiles; i++) diff --git a/modules/cpu/fb/TileError.h b/modules/cpu/fb/TileError.h index fc52bd0538..e54a7ef036 100644 --- a/modules/cpu/fb/TileError.h +++ b/modules/cpu/fb/TileError.h @@ -1,10 +1,11 @@ -// Copyright 2009-2019 Intel Corporation +// Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once #include #include "common/OSPCommon.h" +#include "rkcommon/containers/AlignedVector.h" namespace ospray { @@ -15,16 +16,20 @@ class OSPRAY_SDK_INTERFACE TileError { public: TileError(const vec2i &numTiles); - ~TileError(); + void clear(); + float operator[](const vec2i &tile) const; + void update(const vec2i &tile, const float error); + float refine(const float errorThreshold); protected: vec2i numTiles; int tiles; - float *tileErrorBuffer; // holds error per tile + // holds error per tile + containers::AlignedVector tileErrorBuffer; // image regions (in #tiles) which do not yet estimate the error on // per-tile base std::vector errorRegion; diff --git a/modules/mpi/ospray/fb/DistributedFrameBuffer.cpp b/modules/mpi/ospray/fb/DistributedFrameBuffer.cpp index c9ffaa273b..117f3efbf5 100644 --- a/modules/mpi/ospray/fb/DistributedFrameBuffer.cpp +++ b/modules/mpi/ospray/fb/DistributedFrameBuffer.cpp @@ -37,7 +37,7 @@ void DistributedTileError::sync() if (tiles <= 0) return; - mpicommon::bcast(tileErrorBuffer, tiles, MPI_FLOAT, 0, group.comm).wait(); + mpicommon::bcast(tileErrorBuffer.data(), tiles, MPI_FLOAT, 0, group.comm).wait(); } // DistributedFrameBuffer definitions /////////////////////////////////////// From 0a2ecdfd256d2f086d8b359daf1a03924cd62826 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20G=C3=BCnther?= Date: Fri, 17 Dec 2021 16:38:31 +0100 Subject: [PATCH 13/26] Allow rebinding of group for instance, fix it for of geometry/volume --- CHANGELOG.md | 1 + apps/ospTutorial/ospTutorialAsync.c | 8 ++++--- doc/api.md | 26 ++++++++++++++------- doc/links.md | 2 ++ modules/cpu/ISPCDevice.cpp | 1 + modules/cpu/common/Instance.cpp | 8 ++++--- modules/cpu/common/Instance.h | 1 + modules/cpu/geometry/GeometricModel.cpp | 21 +++++++---------- modules/cpu/geometry/GeometricModel.h | 2 +- modules/cpu/volume/VolumetricModel.cpp | 15 ++++-------- modules/cpu/volume/VolumetricModel.h | 2 +- modules/mpi/ospray/MPIDistributedDevice.cpp | 1 + ospray/api/API.cpp | 3 --- ospray/include/ospray/ospray.h | 8 +++---- scripts/superbuild/CMakeLists.txt | 2 +- 15 files changed, 54 insertions(+), 47 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4db98bedbf..d24b79c488 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ Version History - OSPRAY_APPS_TESTING refactored to OSPRAY_ENABLE_APPS_TESTING - Changed from area sampling to solid angle sampling for quad lights in the pathtracer +- Instances can now have the group object rebound via a parameter ### Changes in v2.8.0: diff --git a/apps/ospTutorial/ospTutorialAsync.c b/apps/ospTutorial/ospTutorialAsync.c index 3cb88aa438..320446ac27 100644 --- a/apps/ospTutorial/ospTutorialAsync.c +++ b/apps/ospTutorial/ospTutorialAsync.c @@ -1,4 +1,4 @@ -// Copyright 2009-2020 Intel Corporation +// Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 /* This is a small example tutorial of how to use OSPRay's async API in an @@ -374,7 +374,8 @@ void buildScene2(OSPCamera *camera, // put the mesh into a model static OSPGeometricModel model; - model = ospNewGeometricModel(mesh); + model = ospNewGeometricModel(NULL); + ospSetObject(model, "geometry", mesh); ospCommit(model); ospRelease(mesh); @@ -388,7 +389,8 @@ void buildScene2(OSPCamera *camera, // put the group into an instance (give the group a world transform) static OSPInstance instance; - instance = ospNewInstance(group); + instance = ospNewInstance(NULL); + ospSetObject(instance, "group", group); ospCommit(instance); ospRelease(group); diff --git a/doc/api.md b/doc/api.md index e5c0667de6..76d67b531b 100644 --- a/doc/api.md +++ b/doc/api.md @@ -1021,17 +1021,20 @@ the volume (and possible acceleration structures it contains) to rendering-specific parameters (where more than one set may exist concurrently). To create a volume instance, call - OSPVolumetricModel ospNewVolumetricModel(OSPVolume volume); + OSPVolumetricModel ospNewVolumetricModel(OSPVolume); The passed volume can be `NULL` as long as the volume to be used is -passed as a parameter. If both a volume is specified on object creation and -as a parameter, the parameter value is used. If the parameter value +passed as a parameter. If both a volume is specified on object creation +and as a parameter, the parameter value is used. If the parameter value is later removed, the volume object passed on object creation is again used. -------------------- ----------------- -------- -------------------------------------- Type Name Default Description -------------------- ----------------- -------- -------------------------------------- + OSPVolume volume optional [volume] object this model + references + OSPTransferFunction transferFunction [transfer function] to use float densityScale 1.0 makes volumes uniformly thinner or @@ -1040,9 +1043,6 @@ used. float anisotropy 0.0 anisotropy of the (Henyey-Greenstein) phase function in [-1ā€“1] ([path tracer] only), default to isotropic scattering - - OSPVolume volume optional [volume] object this model - references -------------------- ----------------- -------- --------------------------------------- : Parameters understood by VolumetricModel. @@ -1342,7 +1342,7 @@ GeometricModels. These take a geometry, which defines the surface representation, and applies either full-object or per-primitive color and material information. To create a geometric model, call - OSPGeometricModel ospNewGeometricModel(OSPGeometry geometry); + OSPGeometricModel ospNewGeometricModel(OSPGeometry); The passed geometry can be `NULL` as long as the geometry to be used is passed as a parameter. If both a geometry is specified on object creation @@ -1367,6 +1367,8 @@ with normals oriented outside clips everything what's inside. ------------------------ -------------- ---------------------------------------------------- Type Name Description ------------------------ -------------- ---------------------------------------------------- + OSPGeometry geometry optional [geometry] object this model references + OSPMaterial / uint32 material optional [material] applied to the geometry, may be an index into the `material` parameter on the [renderer] (if it exists) @@ -1385,8 +1387,6 @@ with normals oriented outside clips everything what's inside. `color` and `material` bool invertNormals inverts all shading normals (Ns), default false - - OSPGeometry geometry optional [geometry] object this model references ------------------------ -------------- ---------------------------------------------------- : Parameters understood by GeometricModel. @@ -1772,9 +1772,17 @@ via a transform. To create and instance call OSPInstance ospNewInstance(OSPGroup); +The passed group can be `NULL` as long as the group to be instanced is +passed as a parameter. If both a group is specified on object creation +and as a parameter, the parameter value is used. If the parameter value +is later removed, the group object passed on object creation is again +used. + ------------ ----------------- ---------- -------------------------------------------------------- Type Name Default Description ------------ ----------------- ---------- -------------------------------------------------------- + OSPGroup group optional [group] object to be instanced + affine3f transform identity world-space transform for all attached geometries and volumes, overridden by `motion.*` arrays diff --git a/doc/links.md b/doc/links.md index ca9e112d53..b82ed5b3da 100644 --- a/doc/links.md +++ b/doc/links.md @@ -6,8 +6,10 @@ [doc/ospray2_porting_guide.md]: https://www.ospray.org/ospray2_porting_guide.html [camera]: documentation.html#cameras +[geometry]: documentation.html#geometries [volume]: documentation.html#volumes [curves]: documentation.html#curves +[group]: documentation.html#groups [world]: documentation.html#world [data]: documentation.html#data [GeometricModel]: documentation.html#geometricmodels diff --git a/modules/cpu/ISPCDevice.cpp b/modules/cpu/ISPCDevice.cpp index 0ce0e3d9dd..7b982f60a5 100644 --- a/modules/cpu/ISPCDevice.cpp +++ b/modules/cpu/ISPCDevice.cpp @@ -83,6 +83,7 @@ static std::map> setParamFcns = { declare_param_setter_object(Data *), declare_param_setter_object(FrameBuffer *), declare_param_setter_object(Future *), + declare_param_setter_object(Geometry *), declare_param_setter_object(GeometricModel *), declare_param_setter_object(Group *), declare_param_setter_object(ImageOp *), diff --git a/modules/cpu/common/Instance.cpp b/modules/cpu/common/Instance.cpp index 88273f3924..679109e4a9 100644 --- a/modules/cpu/common/Instance.cpp +++ b/modules/cpu/common/Instance.cpp @@ -11,12 +11,10 @@ namespace ospray { -Instance::Instance(Group *_group) +Instance::Instance(Group *_group) : groupAPI(_group) { managedObjectType = OSP_INSTANCE; this->ispcEquivalent = ispc::Instance_create(); - - group = _group; } std::string Instance::toString() const @@ -26,6 +24,10 @@ std::string Instance::toString() const void Instance::commit() { + group = hasParam("group") ? (Group *)getParamObject("group") : groupAPI; + if (!group) + throw std::runtime_error(toString() + " received NULL 'group'"); + motionTransform.readParams(*this); ispc::Instance_set(getIE(), diff --git a/modules/cpu/common/Instance.h b/modules/cpu/common/Instance.h index 62074e9cc4..2d12a13c9c 100644 --- a/modules/cpu/common/Instance.h +++ b/modules/cpu/common/Instance.h @@ -23,6 +23,7 @@ struct OSPRAY_SDK_INTERFACE Instance : public ManagedObject box3f getBounds() const override; Ref group; + const Ref groupAPI; MotionTransform motionTransform; }; diff --git a/modules/cpu/geometry/GeometricModel.cpp b/modules/cpu/geometry/GeometricModel.cpp index b9ad582197..8870e8b630 100644 --- a/modules/cpu/geometry/GeometricModel.cpp +++ b/modules/cpu/geometry/GeometricModel.cpp @@ -10,10 +10,9 @@ namespace ospray { -GeometricModel::GeometricModel(Geometry *_geometry) +GeometricModel::GeometricModel(Geometry *_geometry) : geomAPI(_geometry) { managedObjectType = OSP_GEOMETRIC_MODEL; - geomAPI = _geometry; this->ispcEquivalent = ispc::GeometricModel_create(); } @@ -24,13 +23,10 @@ std::string GeometricModel::toString() const void GeometricModel::commit() { - if (hasParam("geometry")) - geom = (Geometry *)getParamObject("geometry"); - else - geom = geomAPI; - - if (geom.ptr == nullptr) - throw std::runtime_error("geometric model received null geometry"); + geom = + hasParam("geometry") ? (Geometry *)getParamObject("geometry") : geomAPI; + if (!geom) + throw std::runtime_error(toString() + " received NULL 'geometry'"); bool useRendererMaterialList = false; materialData = getParamDataT("material", false, true); @@ -54,7 +50,8 @@ void GeometricModel::commit() size_t maxItems = geom->numPrimitives(); if (indexData && indexData->size() < maxItems) { postStatusMsg(OSP_LOG_INFO) - << toString() << " not enough 'index' elements for geometry, clamping"; + << toString() + << " not enough 'index' elements for 'geometry', clamping"; } if (indexData) @@ -65,13 +62,13 @@ void GeometricModel::commit() postStatusMsg(OSP_LOG_INFO) << toString() << " potentially not enough 'material' elements for " - "geometry, clamping"; + "'geometry', clamping"; } if (colorData && colorData->size() > 1 && colorData->size() < maxItems) { postStatusMsg(OSP_LOG_INFO) << toString() - << " potentially not enough 'color' elements for geometry, clamping"; + << " potentially not enough 'color' elements for 'geometry', clamping"; } invertNormals = getParam("invertNormals"); diff --git a/modules/cpu/geometry/GeometricModel.h b/modules/cpu/geometry/GeometricModel.h index ede6727504..87915231ea 100644 --- a/modules/cpu/geometry/GeometricModel.h +++ b/modules/cpu/geometry/GeometricModel.h @@ -23,7 +23,7 @@ struct OSPRAY_SDK_INTERFACE GeometricModel : public ManagedObject private: Ref geom; - Ref geomAPI; + const Ref geomAPI; Ref materialData; Ref> colorData; Ref> indexData; diff --git a/modules/cpu/volume/VolumetricModel.cpp b/modules/cpu/volume/VolumetricModel.cpp index 937c49649a..34274f7cd2 100644 --- a/modules/cpu/volume/VolumetricModel.cpp +++ b/modules/cpu/volume/VolumetricModel.cpp @@ -7,10 +7,9 @@ namespace ospray { -VolumetricModel::VolumetricModel(Volume *_volume) +VolumetricModel::VolumetricModel(Volume *_volume) : volumeAPI(_volume) { managedObjectType = OSP_VOLUMETRIC_MODEL; - volumeAPI = _volume; } VolumetricModel::~VolumetricModel() @@ -26,19 +25,15 @@ std::string VolumetricModel::toString() const void VolumetricModel::commit() { - if (hasParam("volume")) - volume = (Volume *)getParamObject("volume"); - else - volume = volumeAPI; - - if (volume.ptr == nullptr) - throw std::runtime_error("volumetric model received null volume"); + volume = hasParam("volume") ? (Volume *)getParamObject("volume") : volumeAPI; + if (!volume) + throw std::runtime_error(toString() + " received NULL 'volume'"); auto *transferFunction = (TransferFunction *)getParamObject("transferFunction", nullptr); if (transferFunction == nullptr) - throw std::runtime_error("volumetric model must have 'transferFunction'"); + throw std::runtime_error(toString() + " must have 'transferFunction'"); // create value selector using transfer function and pass to volume if (volume->vklVolume) { diff --git a/modules/cpu/volume/VolumetricModel.h b/modules/cpu/volume/VolumetricModel.h index 36a147f182..50d01ac7c2 100644 --- a/modules/cpu/volume/VolumetricModel.h +++ b/modules/cpu/volume/VolumetricModel.h @@ -34,7 +34,7 @@ struct OSPRAY_SDK_INTERFACE VolumetricModel private: box3f volumeBounds; Ref volume; - Ref volumeAPI; + const Ref volumeAPI; VKLIntervalIteratorContext vklIntervalContext{nullptr}; }; diff --git a/modules/mpi/ospray/MPIDistributedDevice.cpp b/modules/mpi/ospray/MPIDistributedDevice.cpp index 6b75dc5281..ea1fa1c688 100644 --- a/modules/mpi/ospray/MPIDistributedDevice.cpp +++ b/modules/mpi/ospray/MPIDistributedDevice.cpp @@ -73,6 +73,7 @@ static std::map> setParamFcns = { declare_param_setter_object(Data *), declare_param_setter_object(FrameBuffer *), declare_param_setter_object(Future *), + declare_param_setter_object(Geometry *), declare_param_setter_object(GeometricModel *), declare_param_setter_object(Group *), declare_param_setter_object(ImageOp *), diff --git a/ospray/api/API.cpp b/ospray/api/API.cpp index 1ea91a4a47..5a8d93899b 100644 --- a/ospray/api/API.cpp +++ b/ospray/api/API.cpp @@ -437,7 +437,6 @@ OSPRAY_CATCH_END(nullptr) extern "C" OSPGeometricModel ospNewGeometricModel( OSPGeometry geom) OSPRAY_CATCH_BEGIN { - THROW_IF_NULL_OBJECT(geom); ASSERT_DEVICE(); OSPGeometricModel instance = currentDevice().newGeometricModel(geom); return instance; @@ -447,7 +446,6 @@ OSPRAY_CATCH_END(nullptr) extern "C" OSPVolumetricModel ospNewVolumetricModel( OSPVolume volume) OSPRAY_CATCH_BEGIN { - THROW_IF_NULL_OBJECT(volume); ASSERT_DEVICE(); OSPVolumetricModel instance = currentDevice().newVolumetricModel(volume); return instance; @@ -503,7 +501,6 @@ OSPRAY_CATCH_END(nullptr) extern "C" OSPInstance ospNewInstance(OSPGroup group) OSPRAY_CATCH_BEGIN { - THROW_IF_NULL_OBJECT(group); ASSERT_DEVICE(); return currentDevice().newInstance(group); } diff --git a/ospray/include/ospray/ospray.h b/ospray/include/ospray/ospray.h index 140f0b9e76..3560cf0749 100644 --- a/ospray/include/ospray/ospray.h +++ b/ospray/include/ospray/ospray.h @@ -244,14 +244,14 @@ OSPRAY_INTERFACE OSPGeometry ospNewGeometry(const char *type); OSPRAY_INTERFACE OSPVolume ospNewVolume(const char *type); OSPRAY_INTERFACE OSPGeometricModel ospNewGeometricModel( - OSPGeometry geom OSP_DEFAULT_VAL(NULL)); + OSPGeometry OSP_DEFAULT_VAL(NULL)); OSPRAY_INTERFACE OSPVolumetricModel ospNewVolumetricModel( - OSPVolume volume OSP_DEFAULT_VAL(NULL)); + OSPVolume OSP_DEFAULT_VAL(NULL)); // Model Meta-Data ////////////////////////////////////////////////////////// OSPRAY_INTERFACE OSPMaterial ospNewMaterial( - const char */*ignored*/, const char *materialType); + const char * /*ignored*/, const char *materialType); OSPRAY_INTERFACE OSPTransferFunction ospNewTransferFunction(const char *type); @@ -260,7 +260,7 @@ OSPRAY_INTERFACE OSPTexture ospNewTexture(const char *type); // Instancing /////////////////////////////////////////////////////////////// OSPRAY_INTERFACE OSPGroup ospNewGroup(); -OSPRAY_INTERFACE OSPInstance ospNewInstance(OSPGroup); +OSPRAY_INTERFACE OSPInstance ospNewInstance(OSPGroup OSP_DEFAULT_VAL(NULL)); // Top-level Worlds ///////////////////////////////////////////////////////// diff --git a/scripts/superbuild/CMakeLists.txt b/scripts/superbuild/CMakeLists.txt index 1c6d4ed1ff..704d8c35c2 100644 --- a/scripts/superbuild/CMakeLists.txt +++ b/scripts/superbuild/CMakeLists.txt @@ -83,7 +83,7 @@ else() mark_as_advanced(FORCE ISPC_VERSION) endif() -set(RKCOMMON_VERSION "1.8.0" CACHE STRING "Which version of rkcommon to build?") +set(RKCOMMON_VERSION "devel" CACHE STRING "Which version of rkcommon to build?") if (RKCOMMON_VERSION STREQUAL "1.8.0") set(RKCOMMON_HASH "d9593de25a9b705d7d56f53d655c71901a25be21d88aa493d39d2d6e61ed7393") endif() From 9a8963c0d645044446ccb3acff0a74f1d1d53858 Mon Sep 17 00:00:00 2001 From: Will Usher Date: Mon, 20 Dec 2021 10:16:33 -0800 Subject: [PATCH 14/26] Allow rebinding of group for instance, geometry, volume in MultiDevice --- modules/multiDevice/MultiDevice.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/modules/multiDevice/MultiDevice.cpp b/modules/multiDevice/MultiDevice.cpp index 2063d09d52..3c241c105f 100644 --- a/modules/multiDevice/MultiDevice.cpp +++ b/modules/multiDevice/MultiDevice.cpp @@ -45,7 +45,8 @@ void MultiDevice::commit() subdevices.emplace_back(std::move(d)); subdeviceLoadBalancers.push_back(subdevices.back()->loadBalancer); } - loadBalancer = rkcommon::make_unique(subdeviceLoadBalancers); + loadBalancer = + rkcommon::make_unique(subdeviceLoadBalancers); } // ISPCDevice::commit will init the tasking system but here we can reset @@ -173,8 +174,9 @@ OSPGeometricModel MultiDevice::newGeometricModel(OSPGeometry geom) MultiDeviceObject *g = (MultiDeviceObject *)geom; MultiDeviceObject *o = new MultiDeviceObject; for (size_t i = 0; i < subdevices.size(); ++i) { - o->objects.push_back( - subdevices[i]->newGeometricModel((OSPGeometry)g->objects[i])); + OSPGeometry subdeviceObj = + g ? (OSPGeometry)g->objects[i] : (OSPGeometry) nullptr; + o->objects.push_back(subdevices[i]->newGeometricModel(subdeviceObj)); } return (OSPGeometricModel)o; } @@ -183,8 +185,8 @@ OSPVolumetricModel MultiDevice::newVolumetricModel(OSPVolume volume) MultiDeviceObject *v = (MultiDeviceObject *)volume; MultiDeviceObject *o = new MultiDeviceObject; for (size_t i = 0; i < subdevices.size(); ++i) { - o->objects.push_back( - subdevices[i]->newVolumetricModel((OSPVolume)v->objects[i])); + OSPVolume subdeviceObj = v ? (OSPVolume)v->objects[i] : (OSPVolume) nullptr; + o->objects.push_back(subdevices[i]->newVolumetricModel(subdeviceObj)); } return (OSPVolumetricModel)o; } @@ -234,7 +236,8 @@ OSPInstance MultiDevice::newInstance(OSPGroup group) MultiDeviceObject *g = (MultiDeviceObject *)group; MultiDeviceObject *o = new MultiDeviceObject; for (size_t i = 0; i < subdevices.size(); ++i) { - o->objects.push_back(subdevices[i]->newInstance((OSPGroup)g->objects[i])); + OSPGroup subdeviceObj = g ? (OSPGroup)g->objects[i] : (OSPGroup) nullptr; + o->objects.push_back(subdevices[i]->newInstance(subdeviceObj)); } return (OSPInstance)o; } From 9d0966919a8ab29f112ba31f18dd2a7f75b0f18b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20G=C3=BCnther?= Date: Mon, 20 Dec 2021 18:34:53 +0100 Subject: [PATCH 15/26] Add protex scan --- .gitlab-ci.yml | 1 - scripts/scan/protex.sh | 5 +++-- scripts/superbuild/CMakeLists.txt | 4 ++++ scripts/superbuild/anari-ospray.cmake | 11 +++++++++++ 4 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 scripts/superbuild/anari-ospray.cmake diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 41812e4781..482d5fecb0 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -14,7 +14,6 @@ stages: variables: GIT_DEPTH: "15" - KW_PROJECT_NAME: ospray ErrorActionPreference: STOP .job_template: &build_job_docker diff --git a/scripts/scan/protex.sh b/scripts/scan/protex.sh index 9c38e5f877..288897bc26 100755 --- a/scripts/scan/protex.sh +++ b/scripts/scan/protex.sh @@ -1,5 +1,5 @@ #!/bin/bash -## Copyright 2020 Intel Corporation +## Copyright 2020-2021 Intel Corporation ## SPDX-License-Identifier: Apache-2.0 # enable early exit on fail @@ -17,8 +17,9 @@ export _JAVA_OPTIONS=-Duser.home=$PROTEX_PATH/home cd $SRC_PATH # scan additional source -cmake -DBUILD_OSPRAY_MODULE_MPI=ON scripts/superbuild +cmake -DBUILD_OSPRAY_MODULE_MPI=ON -DBUILD_OSPRAY_ANARI=ON scripts/superbuild cmake --build . --target rkcommon-download +cmake --build . --target anari-ospray-download rm -rf CMakeFiles diff --git a/scripts/superbuild/CMakeLists.txt b/scripts/superbuild/CMakeLists.txt index 704d8c35c2..37e020bcb2 100644 --- a/scripts/superbuild/CMakeLists.txt +++ b/scripts/superbuild/CMakeLists.txt @@ -228,6 +228,10 @@ if (BUILD_OSPRAY_MODULE_MULTIDEVICE) set(OSPRAY_MODULE_MULTIDEVICE ON) endif() +if (BUILD_OSPRAY_ANARI) + include(anari-ospray.cmake) +endif() + if (NOT BUILD_DEPENDENCIES_ONLY) include(build_ospray.cmake) endif() diff --git a/scripts/superbuild/anari-ospray.cmake b/scripts/superbuild/anari-ospray.cmake new file mode 100644 index 0000000000..ade9c6372a --- /dev/null +++ b/scripts/superbuild/anari-ospray.cmake @@ -0,0 +1,11 @@ +## Copyright 2021 Intel Corporation +## SPDX-License-Identifier: Apache-2.0 + +set(COMPONENT_NAME anari-ospray) + +ExternalProject_Add(${COMPONENT_NAME} + SOURCE_DIR ${COMPONENT_NAME}/src + GIT_REPOSITORY $ENV{CI_SERVER_PROTOCOL}://gitlab-ci-token:$ENV{CI_JOB_TOKEN}@$ENV{CI_SERVER_HOST}/$ENV{CI_PROJECT_NAMESPACE}/${COMPONENT_NAME}.git + GIT_TAG origin/devel +) +ExternalProject_Add_StepTargets(${COMPONENT_NAME} NO_DEPENDS download) From e9ed53ec5029de352429616c40cd0f35c4e421f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20G=C3=BCnther?= Date: Fri, 14 Jan 2022 13:57:14 +0000 Subject: [PATCH 16/26] Fix spelling --- CHANGELOG.md | 2 +- README.md | 2 +- STYLEGUIDE.md | 2 +- apps/ospExamples/GLFWOSPRayWindow.cpp | 6 ++-- cmake/compiler/ispc.cmake | 4 +-- doc/api.md | 2 +- doc/gallery.md | 2 +- doc/ospray-doc | 2 +- doc/ospray2_porting_guide.md | 2 +- doc/related_projects.md | 2 +- modules/cpu/camera/Camera.h | 4 +-- modules/cpu/common/Data.h | 6 ++-- modules/cpu/common/Data.ih | 4 +-- modules/cpu/common/World.ih | 6 ++-- modules/cpu/fb/FrameBufferView.h | 4 +-- modules/cpu/fb/frame_ops/SSAO.ispc | 4 +-- modules/cpu/geometry/Geometry.h | 4 +-- modules/cpu/geometry/Isosurfaces.h | 4 +-- modules/cpu/geometry/Isosurfaces.ispc | 6 ++-- modules/cpu/geometry/Planes.ispc | 4 +-- modules/cpu/lights/AmbientLight.ispc | 4 +-- modules/cpu/lights/HDRILight.ispc | 4 +-- modules/cpu/lights/Light.h | 4 +-- modules/cpu/lights/PointLight.ispc | 4 +-- modules/cpu/lights/QuadLight.ispc | 4 +-- modules/cpu/lights/SpotLight.cpp | 4 +-- modules/cpu/lights/SpotLight.ispc | 4 +-- modules/cpu/lights/sky_model/sky_model.cpp | 4 +-- modules/cpu/lights/sky_model/sky_model.h | 6 ++-- modules/cpu/render/Material.h | 4 +-- modules/cpu/render/ao/volumes.ispc | 6 ++-- .../cpu/render/pathtracer/GeometryLight.ispc | 4 +-- modules/cpu/render/scivis/surfaces.ispc | 4 +-- modules/cpu/render/scivis/volumes.ispc | 6 ++-- modules/cpu/texture/Texture.h | 4 +-- modules/cpu/volume/Volume.cpp | 4 +-- modules/cpu/volume/VolumetricModel.cpp | 6 ++-- .../transferFunction/TransferFunction.h | 4 +-- modules/mpi/common/Collectives.h | 4 +-- modules/mpi/ospray/common/OSPWork.h | 4 +-- .../mpi/ospray/fb/DistributedFrameBuffer.cpp | 4 +-- .../mpi/ospray/fb/DistributedFrameBuffer.h | 4 +-- .../fb/DistributedFrameBuffer_TileMessages.h | 4 +-- modules/mpi/tutorials/ospMPIDistribTutorial.c | 4 +-- .../mpi/tutorials/ospMPIDistribTutorial.cpp | 4 +-- .../tutorials/ospMPIDistribTutorialAsync.c | 4 +-- .../multiDevice/MultiDeviceLoadBalancer.cpp | 9 ++++-- ospray/api/Device.h | 4 +-- ospray/common/OSPCommon.cpp | 32 +++++++++---------- ospray/include/ospray/ospray.h | 4 +-- ospray/include/ospray/ospray_cpp/Data.h | 4 +-- scripts/superbuild/README.md | 2 +- scripts/tests/run_tests.ps1 | 4 +-- 53 files changed, 123 insertions(+), 120 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d24b79c488..bdc611099a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -399,7 +399,7 @@ Version History is required to build OSPRay - The MPI module is now a separate repository, which also contains all MPI distributed rendering documentation -- Log levels are now controled with enums and named strings (where +- Log levels are now controlled with enums and named strings (where applicable) - A new flag was also introduced which turns all OSP_LOG_WARNING messages into errors, which are submitted to the error callback diff --git a/README.md b/README.md index 0546946951..7156cad995 100644 --- a/README.md +++ b/README.md @@ -2328,7 +2328,7 @@ Parameters of the Glass material. For convenience, the rather counter-intuitive physical attenuation coefficients will be calculated from the user inputs in such a way, that the `attenuationColor` will be the result when white light traveled -trough a glass of thickness `attenuationDistance`. +through a glass of thickness `attenuationDistance`.
Rendering of a Glass material with orange attenuation. diff --git a/STYLEGUIDE.md b/STYLEGUIDE.md index 0fe4314d79..1c56c67e30 100644 --- a/STYLEGUIDE.md +++ b/STYLEGUIDE.md @@ -157,7 +157,7 @@ The following are concerns specific to ISPC: ``` - Prefer omitting `uniform`/`varying` qualifiers for structs. Only - apply them if they need to be consistenly the same for both + apply them if they need to be consistently the same for both `uniform` and `varying` instances of the struct. ### OSPRay ISPCDevice Specific Design Choices diff --git a/apps/ospExamples/GLFWOSPRayWindow.cpp b/apps/ospExamples/GLFWOSPRayWindow.cpp index 63f03bc6a9..d1923d61e2 100644 --- a/apps/ospExamples/GLFWOSPRayWindow.cpp +++ b/apps/ospExamples/GLFWOSPRayWindow.cpp @@ -1,4 +1,4 @@ -// Copyright 2018-2021 Intel Corporation +// Copyright 2018-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "GLFWOSPRayWindow.h" @@ -566,7 +566,7 @@ void GLFWOSPRayWindow::buildUI() ImGui::Separator(); - // the gaussian pixel fiter is the default, + // the gaussian pixel filter is the default, // which is at position 2 in the list static int whichPixelFilter = 2; if (ImGui::Combo("pixelfilter##whichPixelFilter", @@ -607,7 +607,7 @@ void GLFWOSPRayWindow::buildUI() addObjectToCommit(renderer->handle()); } - static vec3f bgColorSRGB{0.0f}; // imGUI's widget implicitely uses sRGB + static vec3f bgColorSRGB{0.0f}; // imGUI's widget implicitly uses sRGB if (ImGui::ColorEdit3("backgroundColor", bgColorSRGB)) { bgColor = vec3f(std::pow(bgColorSRGB.x, 2.2f), std::pow(bgColorSRGB.y, 2.2f), diff --git a/cmake/compiler/ispc.cmake b/cmake/compiler/ispc.cmake index 991f193afd..77ba4c8ca2 100644 --- a/cmake/compiler/ispc.cmake +++ b/cmake/compiler/ispc.cmake @@ -1,7 +1,7 @@ -## Copyright 2009-2021 Intel Corporation +## Copyright 2009-2022 Intel Corporation ## SPDX-License-Identifier: Apache-2.0 -# ISPC versions to look for, in decending order (newest first) +# ISPC versions to look for, in descending order (newest first) set(ISPC_VERSION_WORKING "1.16.1" "1.16.0") list(GET ISPC_VERSION_WORKING -1 ISPC_VERSION_REQUIRED) diff --git a/doc/api.md b/doc/api.md index 76d67b531b..2578562a0c 100644 --- a/doc/api.md +++ b/doc/api.md @@ -2392,7 +2392,7 @@ the type string "`glass`" to `ospNewMaterial`. Its parameters are For convenience, the rather counter-intuitive physical attenuation coefficients will be calculated from the user inputs in such a way, that the `attenuationColor` will be the result when white light traveled -trough a glass of thickness `attenuationDistance`. +through a glass of thickness `attenuationDistance`. ![Rendering of a Glass material with orange attenuation.][imgMaterialGlass] diff --git a/doc/gallery.md b/doc/gallery.md index d02dd5461f..905b133052 100644 --- a/doc/gallery.md +++ b/doc/gallery.md @@ -60,7 +60,7 @@ Moran of NASA Ames.
[![Stellar Radiation Video](gallery/thumbnails/stellar_video-thumb.jpg)](https://vimeo.com/417360212 "Stellar Radiation Video")
-Stellar Radation. Visualization by Greg P. Johnson, Intel, and Joseph +Stellar Radiation. Visualization by Greg P. Johnson, Intel, and Joseph Insley, Argonne National Lab, using OSPRay. Simulation provided by the University of California, Santa Barbara (team leads Yan-Fei Jiang and Lars Bildsten). diff --git a/doc/ospray-doc b/doc/ospray-doc index 00a81c08f7..43ab7ded0e 160000 --- a/doc/ospray-doc +++ b/doc/ospray-doc @@ -1 +1 @@ -Subproject commit 00a81c08f722cf890bc37e5588fbc1058cd3819e +Subproject commit 43ab7ded0e586b5e1014bd75904a9c7666ee5bf3 diff --git a/doc/ospray2_porting_guide.md b/doc/ospray2_porting_guide.md index 18f9e62423..56add77823 100644 --- a/doc/ospray2_porting_guide.md +++ b/doc/ospray2_porting_guide.md @@ -111,7 +111,7 @@ provides more fine control over appearance information and instance transformations. In OSPRay v1.x, geometries and volumes contained both structural and -appearance information which limited their reuse in other objets. For +appearance information which limited their reuse in other objects. For example, the volume's transfer function can now be different between an isosurface, slice, and rendered volume all in the same scene without duplicating the actual volume itself. diff --git a/doc/related_projects.md b/doc/related_projects.md index a1d0d5ea76..749e1b72cb 100644 --- a/doc/related_projects.md +++ b/doc/related_projects.md @@ -76,7 +76,7 @@ is integrated as one its renderers. For more information see VMD's pCon.planner --- -[pCon.planner](https://pcon-planner.com/en/) is an architectural design and rendering applicaiton that +[pCon.planner](https://pcon-planner.com/en/) is an architectural design and rendering application that utilizes OSPRay for path traced photorealistic renderings. diff --git a/modules/cpu/camera/Camera.h b/modules/cpu/camera/Camera.h index d422b3422d..a84345d5f1 100644 --- a/modules/cpu/camera/Camera.h +++ b/modules/cpu/camera/Camera.h @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once @@ -59,7 +59,7 @@ struct OSPRAY_SDK_INTERFACE Camera : public ManagedObject OSPTYPEFOR_SPECIALIZATION(Camera *, OSP_CAMERA); -// Inlined defintions ///////////////////////////////////////////////////////// +// Inlined definitions ///////////////////////////////////////////////////////// template inline void Camera::registerType(const char *type) diff --git a/modules/cpu/common/Data.h b/modules/cpu/common/Data.h index 4f1426fb43..a15e552ed3 100644 --- a/modules/cpu/common/Data.h +++ b/modules/cpu/common/Data.h @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once @@ -157,7 +157,7 @@ struct DataT : public Data { static_assert(DIM == 2 || DIM == 3, "only 1D, 2D or 3D DataT supported"); using value_type = T; - using interator = Iter; + using iterator = Iter; Iter begin() const { @@ -187,7 +187,7 @@ template struct DataT : public Data { using value_type = T; - using interator = Iter1D; + using iterator = Iter1D; Iter1D begin() const { diff --git a/modules/cpu/common/Data.ih b/modules/cpu/common/Data.ih index d68115510e..0ec9dd7675 100644 --- a/modules/cpu/common/Data.ih +++ b/modules/cpu/common/Data.ih @@ -1,4 +1,4 @@ -// Copyright 2009-2020 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once @@ -11,7 +11,7 @@ struct Data1D uint8 *addr; int64 byteStride; uint32 numItems; - bool huge; // 64bit address calculation neccessary + bool huge; // 64bit address calculation necessary }; inline void Data1D_Constructor(Data1D *uniform self) diff --git a/modules/cpu/common/World.ih b/modules/cpu/common/World.ih index 62588dc597..5e8207e978 100644 --- a/modules/cpu/common/World.ih +++ b/modules/cpu/common/World.ih @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once @@ -154,7 +154,7 @@ inline void traceClippingRay(const World *uniform world, // Intersect all geometry along given ray, // we have to temporarily extend the ray to inf because - // even distant intersections affect visibilty of close objects + // even distant intersections affect visibility of close objects float origT = ray.t; ray.t = inf; rtcIntersectV(world->embreeSceneHandleClippers, @@ -185,7 +185,7 @@ inline void traceClippingRay(const World *uniform world, if (absT > ray.t) break; - // Check if comming into or out of clipping area + // Check if coming into or out of clipping area if (t < 0.0f) { clippingDepth--; // out of clipping area } else { diff --git a/modules/cpu/fb/FrameBufferView.h b/modules/cpu/fb/FrameBufferView.h index 11a538ab25..264cace6a9 100644 --- a/modules/cpu/fb/FrameBufferView.h +++ b/modules/cpu/fb/FrameBufferView.h @@ -1,4 +1,4 @@ -// Copyright 2009-2020 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once @@ -9,7 +9,7 @@ namespace ospray { struct FrameBuffer; -/*! A view into a portion of the frambuffer to run the frame operation on +/*! A view into a portion of the framebuffer to run the frame operation on */ struct OSPRAY_SDK_INTERFACE FrameBufferView { diff --git a/modules/cpu/fb/frame_ops/SSAO.ispc b/modules/cpu/fb/frame_ops/SSAO.ispc index b984434f77..2d132aa02f 100644 --- a/modules/cpu/fb/frame_ops/SSAO.ispc +++ b/modules/cpu/fb/frame_ops/SSAO.ispc @@ -1,4 +1,4 @@ -// Copyright 2009-2020 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // ospray @@ -86,7 +86,7 @@ export void LiveSSAOFrameOp_getOcclusion(const void *uniform _self, pixelPosCamera = pixelPosCamera * ((pointDepth) / (length(pixelPosCamera))); pixelPosCamera = xfmPoint(rcp(self->cameraSpace), pixelPosCamera); - // apply kernal + // apply kernel for (uint32 k = 0; k < kernelSize; ++k) { // calculate TBN matrix vec3f tangent = normalize(self->randomVecs[k] diff --git a/modules/cpu/geometry/Geometry.h b/modules/cpu/geometry/Geometry.h index 0b1f21b41e..849a915f62 100644 --- a/modules/cpu/geometry/Geometry.h +++ b/modules/cpu/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 @@ -43,7 +43,7 @@ struct OSPRAY_SDK_INTERFACE Geometry : public ManagedObject OSPTYPEFOR_SPECIALIZATION(Geometry *, OSP_GEOMETRY); -// Inlined defintions ///////////////////////////////////////////////////////// +// Inlined definitions ///////////////////////////////////////////////////////// template inline void Geometry::registerType(const char *type) diff --git a/modules/cpu/geometry/Isosurfaces.h b/modules/cpu/geometry/Isosurfaces.h index 77350c90e5..98367a98cc 100644 --- a/modules/cpu/geometry/Isosurfaces.h +++ b/modules/cpu/geometry/Isosurfaces.h @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once @@ -23,7 +23,7 @@ struct OSPRAY_SDK_INTERFACE Isosurfaces : public Geometry // Data members // Ref> isovaluesData; - // For backwards compatability, a volumetric model was used to set + // For backwards compatibility, a volumetric model was used to set // the volume and color Ref model; Ref volume; diff --git a/modules/cpu/geometry/Isosurfaces.ispc b/modules/cpu/geometry/Isosurfaces.ispc index a983b06032..11a7888161 100644 --- a/modules/cpu/geometry/Isosurfaces.ispc +++ b/modules/cpu/geometry/Isosurfaces.ispc @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // ospray @@ -14,7 +14,7 @@ struct Isosurfaces { Geometry super; //!< inherited geometry fields float *isovalues; - // For backwards compatability, a volumetric model was used to set + // For backwards compatibility, a volumetric model was used to set // the volume and color VolumetricModel *volumetricModel; Volume *volume; @@ -31,7 +31,7 @@ int Isosurfaces_primID(const Isosurfaces *uniform self, const float isovalue) return -1; } -// A wrapper for backwards compatability with the old VolumetricModel API +// A wrapper for backwards compatibility with the old VolumetricModel API Volume *uniform Isosurfaces_getVolume(const Isosurfaces *uniform self) { if (self->volume) { diff --git a/modules/cpu/geometry/Planes.ispc b/modules/cpu/geometry/Planes.ispc index 21595d0e0d..6d957ffca3 100644 --- a/modules/cpu/geometry/Planes.ispc +++ b/modules/cpu/geometry/Planes.ispc @@ -1,4 +1,4 @@ -// Copyright 2019-2021 Intel Corporation +// Copyright 2019-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // ospray @@ -11,7 +11,7 @@ #include "rkcommon/math/box.ih" #include "rkcommon/math/vec.ih" -// The plane can't be truely infinite because Embree has a limit on object size +// The plane can't be truly infinite because Embree has a limit on object size #define PLANE_MAX_SIZE 1e18 struct Planes diff --git a/modules/cpu/lights/AmbientLight.ispc b/modules/cpu/lights/AmbientLight.ispc index 0acbbade66..d37828f1b3 100644 --- a/modules/cpu/lights/AmbientLight.ispc +++ b/modules/cpu/lights/AmbientLight.ispc @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "Light.ih" @@ -8,7 +8,7 @@ struct AmbientLight { Light super; //!< inherited light fields - vec3f radiance; //!< emmitted RGB radiance + vec3f radiance; //!< emitted RGB radiance }; // Implementation diff --git a/modules/cpu/lights/HDRILight.ispc b/modules/cpu/lights/HDRILight.ispc index 6d7d444f8b..747d85d8ee 100644 --- a/modules/cpu/lights/HDRILight.ispc +++ b/modules/cpu/lights/HDRILight.ispc @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "Light.ih" @@ -19,7 +19,7 @@ struct HDRILight const Distribution2D *uniform distribution; // The 2D distribution used to importance sample vec2f rcpSize; // precomputed 1/map.size - vec3f radianceScale; // scaling factor of emmitted RGB radiance + vec3f radianceScale; // scaling factor of emitted RGB radiance }; // Implementation diff --git a/modules/cpu/lights/Light.h b/modules/cpu/lights/Light.h index eae6741175..118ad7c926 100644 --- a/modules/cpu/lights/Light.h +++ b/modules/cpu/lights/Light.h @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once @@ -38,7 +38,7 @@ struct OSPRAY_SDK_INTERFACE Light : public ManagedObject OSPTYPEFOR_SPECIALIZATION(Light *, OSP_LIGHT); -// Inlined defintions ///////////////////////////////////////////////////////// +// Inlined definitions ///////////////////////////////////////////////////////// template inline void Light::registerType(const char *type) diff --git a/modules/cpu/lights/PointLight.ispc b/modules/cpu/lights/PointLight.ispc index b27cbb337b..cec146f559 100644 --- a/modules/cpu/lights/PointLight.ispc +++ b/modules/cpu/lights/PointLight.ispc @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "Light.ih" @@ -14,7 +14,7 @@ struct PointLight vec3f position; //!< light position vec3f intensity; //!< RGB color and intensity of light - vec3f radiance; //!< emmitted RGB radiance + vec3f radiance; //!< emitted RGB radiance float radius; //!< defines the size of the SphereLight }; diff --git a/modules/cpu/lights/QuadLight.ispc b/modules/cpu/lights/QuadLight.ispc index f03eef54e8..612f0ce77a 100644 --- a/modules/cpu/lights/QuadLight.ispc +++ b/modules/cpu/lights/QuadLight.ispc @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "Light.ih" @@ -9,7 +9,7 @@ struct QuadLight { Light super; //!< inherited light fields - vec3f radiance; //!< emmitted RGB radiance + vec3f radiance; //!< emitted RGB radiance vec3f position; //!< world-space corner position of the light vec3f edge1; //!< vectors to adjacent corners vec3f edge2; //!< vectors to adjacent corners diff --git a/modules/cpu/lights/SpotLight.cpp b/modules/cpu/lights/SpotLight.cpp index 6c3f13c74e..1c6d33f232 100644 --- a/modules/cpu/lights/SpotLight.cpp +++ b/modules/cpu/lights/SpotLight.cpp @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "SpotLight.h" @@ -101,7 +101,7 @@ void SpotLight::processIntensityQuantityType(const float openingAngle) } else if (intensityQuantity == OSP_INTENSITY_QUANTITY_POWER) { // since our spot light implementation includes the cosine term // we need to consider the integrated cosine cap instead of the - // usally used integrated cap. + // usually used integrated cap. radIntensity = coloredIntensity / sphericalCapCosInt; if (radius > 0.0f) { radiance = coloredIntensity / (sphericalCapCosInt * ringDiskArea); diff --git a/modules/cpu/lights/SpotLight.ispc b/modules/cpu/lights/SpotLight.ispc index 19d3cf6374..2c955fff05 100644 --- a/modules/cpu/lights/SpotLight.ispc +++ b/modules/cpu/lights/SpotLight.ispc @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "Light.ih" @@ -16,7 +16,7 @@ struct SpotLight // SpotLight is emitting linear3f world2light; vec3f intensity; // RGB radiative intensity of the SpotLight - vec3f radiance; // emmitted RGB radiance + vec3f radiance; // emitted RGB radiance float cosAngleMax; // Angular limit of the spot in an easier to use form: // cosine of the half angle in radians float cosAngleScale; // 1/(cos(border of the penumbra area) - cosAngleMax); diff --git a/modules/cpu/lights/sky_model/sky_model.cpp b/modules/cpu/lights/sky_model/sky_model.cpp index 1442c11a1d..8244276e9c 100644 --- a/modules/cpu/lights/sky_model/sky_model.cpp +++ b/modules/cpu/lights/sky_model/sky_model.cpp @@ -28,7 +28,7 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // with slight modifications -// Copyright 2020 Intel Corporation +// Copyright 2020-2022 Intel Corporation /* ============================================================================ @@ -452,7 +452,7 @@ ArHosekSkyModelState *arhosekskymodelstate_alienworld_alloc_init( // more sensitive to could be introduced here - think V(lambda). But // given that the whole effort is not *that* accurate to begin with (we // are talking about the appearance of alien worlds, after all), simple - // averaging over the visible wavelenghts (! - this is why we start at + // averaging over the visible wavelengths (! - this is why we start at // WL #2, and only use 2-11) seems like a sane first approximation. float correctionFactor = 0.0; diff --git a/modules/cpu/lights/sky_model/sky_model.h b/modules/cpu/lights/sky_model/sky_model.h index 69e1d17386..bf5f9ae7b9 100644 --- a/modules/cpu/lights/sky_model/sky_model.h +++ b/modules/cpu/lights/sky_model/sky_model.h @@ -28,7 +28,7 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // with slight modifications -// Copyright 2020 Intel Corporation +// Copyright 2020-2022 Intel Corporation /* ============================================================================ @@ -135,7 +135,7 @@ function which generates skydome states for different solar emission spectra and solar radii: 'arhosekskymodelstate_alienworld_alloc_init()'. See the notes about the "Alien World" functionality provided further down for a -discussion of the usefulness and limits of that second initalisation function. +discussion of the usefulness and limits of that second initialisation function. Sky model states that have been initialised with either function behave in a completely identical fashion during use and cleanup. @@ -368,7 +368,7 @@ ArHosekSkyModelState *arhosekskymodelstate_alloc_init( with a sun of a surface temperature given in 'kelvin'. The parameter 'solar_intensity' controls the overall brightness of the sky, relative to the solar irradiance on Earth. A value of 1.0 yields a sky dome that - is, on average over the wavelenghts covered in the model (!), as bright + is, on average over the wavelengths covered in the model (!), as bright as the terrestrial sky in radiometric terms. Which means that the solar radius has to be adjusted, since the diff --git a/modules/cpu/render/Material.h b/modules/cpu/render/Material.h index c55de9f7b2..eb48840b9d 100644 --- a/modules/cpu/render/Material.h +++ b/modules/cpu/render/Material.h @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once @@ -78,7 +78,7 @@ struct OSPRAY_SDK_INTERFACE Material OSPTYPEFOR_SPECIALIZATION(Material *, OSP_MATERIAL); -// Inlined defintions ///////////////////////////////////////////////////////// +// Inlined definitions ///////////////////////////////////////////////////////// template inline void Material::registerType( diff --git a/modules/cpu/render/ao/volumes.ispc b/modules/cpu/render/ao/volumes.ispc index 47a173ea81..3cfe4c7ce6 100644 --- a/modules/cpu/render/ao/volumes.ispc +++ b/modules/cpu/render/ao/volumes.ispc @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "volumes.ih" @@ -44,7 +44,7 @@ static void sampleVolume(VolumeContext &vc, // Make it local for the next interval vc.iuDistance -= vc.iuLength; - // Calulate how many steps can be made within this interval + // Calculate how many steps can be made within this interval const float samplingStep = vc.interval.nominalDeltaT / samplingRate; vc.iuLength = (vc.interval.tRange.upper - vc.interval.tRange.lower) / samplingStep; @@ -203,7 +203,7 @@ vec4f integrateVolumeIntervals(const VolumeIntervals &volumeIntervals, float time = 0.5f; VolumetricModel *varying model = vi.volumetricModel; foreach_unique (m in model) { - // Create volume interval interator + // Create volume interval iterator vc.intervalIterator = vklInitIntervalIteratorV(m->vklIntervalContext, (varying vkl_vec3f *)&vc.org, (varying vkl_vec3f *)&vc.dir, diff --git a/modules/cpu/render/pathtracer/GeometryLight.ispc b/modules/cpu/render/pathtracer/GeometryLight.ispc index db855f106d..eab35e41e1 100644 --- a/modules/cpu/render/pathtracer/GeometryLight.ispc +++ b/modules/cpu/render/pathtracer/GeometryLight.ispc @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "render/pathtracer/GeometryLight.ih" @@ -23,7 +23,7 @@ vec3f evaluateGeometryLights(const PathContext &pathContext, foreach_unique (m in material) { if (m != NULL && reduce_max(m->emission) > 0.f) { // at the moment we only support uniform light selection - // therfore we can evaluate the selection PDF directly + // therefore we can evaluate the selection PDF directly float lightSelectionPDF = 0.0f; if (pathContext.numLights > 0) { lightSelectionPDF = diff --git a/modules/cpu/render/scivis/surfaces.ispc b/modules/cpu/render/scivis/surfaces.ispc index d93bac9a9c..15661c3b24 100644 --- a/modules/cpu/render/scivis/surfaces.ispc +++ b/modules/cpu/render/scivis/surfaces.ispc @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "math/random.ih" @@ -114,7 +114,7 @@ vec3f evaluateLights( // Iterate through all lights vec3f color = make_vec3f(0.f); for (uniform int i = 0; i < world->scivisData.numLights; i++) { - // Skip lights with disabled visibilty via parameter + // Skip lights with disabled visibility via parameter const Light *uniform l = world->scivisData.lights[i]; if (!l->isVisible) continue; diff --git a/modules/cpu/render/scivis/volumes.ispc b/modules/cpu/render/scivis/volumes.ispc index b7e1f5a0be..9b56f5215a 100644 --- a/modules/cpu/render/scivis/volumes.ispc +++ b/modules/cpu/render/scivis/volumes.ispc @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "surfaces.ih" @@ -51,7 +51,7 @@ static void sampleVolume(SciVisRenderContext &rc, // Make it local for the next interval vc.iuDistance -= vc.iuLength; - // Calulate how many steps can be made within this interval + // Calculate how many steps can be made within this interval const float samplingStep = vc.interval.nominalDeltaT / samplingRate; vc.iuLength = (vc.interval.tRange.upper - vc.interval.tRange.lower) / samplingStep; @@ -242,7 +242,7 @@ vec4f integrateVolumeIntervalsGradient(SciVisRenderContext &rc, float time = 0.5f; VolumetricModel *varying model = vi.volumetricModel; foreach_unique (m in model) { - // Create volume interval interator + // Create volume interval iterator vc.intervalIterator = vklInitIntervalIteratorV(m->vklIntervalContext, (varying vkl_vec3f *)&vc.org, (varying vkl_vec3f *)&vc.dir, diff --git a/modules/cpu/texture/Texture.h b/modules/cpu/texture/Texture.h index 5e8cfd6b43..16614f94a8 100644 --- a/modules/cpu/texture/Texture.h +++ b/modules/cpu/texture/Texture.h @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once @@ -31,7 +31,7 @@ struct OSPRAY_SDK_INTERFACE Texture OSPTYPEFOR_SPECIALIZATION(Texture *, OSP_TEXTURE); -// Inlined defintions ///////////////////////////////////////////////////////// +// Inlined definitions ///////////////////////////////////////////////////////// template inline void Texture::registerType(const char *type) diff --git a/modules/cpu/volume/Volume.cpp b/modules/cpu/volume/Volume.cpp index c2232ccf3c..31cbbd59fa 100644 --- a/modules/cpu/volume/Volume.cpp +++ b/modules/cpu/volume/Volume.cpp @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // ospray @@ -14,7 +14,7 @@ namespace ospray { -// Volume defintions //////////////////////////////////////////////////////// +// Volume definitions //////////////////////////////////////////////////////// Volume::Volume(const std::string &type) : vklType(type) { diff --git a/modules/cpu/volume/VolumetricModel.cpp b/modules/cpu/volume/VolumetricModel.cpp index 34274f7cd2..3ce885008c 100644 --- a/modules/cpu/volume/VolumetricModel.cpp +++ b/modules/cpu/volume/VolumetricModel.cpp @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // ospray @@ -49,7 +49,7 @@ void VolumetricModel::commit() // volume made completely transparent and could be removed, which is // awkward here // set an "empty" interesting value range instead, which will lead to a - // quick out during volume interation + // quick out during volume iteration valueRanges.push_back(range1f(neg_inf, neg_inf)); } VKLData valueRangeData = vklNewData( @@ -58,7 +58,7 @@ void VolumetricModel::commit() vklRelease(valueRangeData); vklCommit(vklIntervalContext); - // Pass interval contex to ISPC + // Pass interval context to ISPC getSh()->vklIntervalContext = vklIntervalContext; } diff --git a/modules/cpu/volume/transferFunction/TransferFunction.h b/modules/cpu/volume/transferFunction/TransferFunction.h index 04a098256d..afd76613f6 100644 --- a/modules/cpu/volume/transferFunction/TransferFunction.h +++ b/modules/cpu/volume/transferFunction/TransferFunction.h @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once @@ -34,7 +34,7 @@ struct OSPRAY_SDK_INTERFACE TransferFunction OSPTYPEFOR_SPECIALIZATION(TransferFunction *, OSP_TRANSFER_FUNCTION); -// Inlined defintions ///////////////////////////////////////////////////////// +// Inlined definitions ///////////////////////////////////////////////////////// template inline void TransferFunction::registerType(const char *type) diff --git a/modules/mpi/common/Collectives.h b/modules/mpi/common/Collectives.h index 50202d2296..c9765afaa7 100644 --- a/modules/mpi/common/Collectives.h +++ b/modules/mpi/common/Collectives.h @@ -1,4 +1,4 @@ -// Copyright 2016-2021 Intel Corporation +// Copyright 2016-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once @@ -330,7 +330,7 @@ class AllReduce : public Collective * With these we actually want the ability to wait for a specific send to * finish, and a specific recv from a process to be completed. With the * fire and forget messaging layer we don't care about this and just queue - * stuff up and recieve whatever is coming to us. + * stuff up and receive whatever is coming to us. */ class Send : public Collective { diff --git a/modules/mpi/ospray/common/OSPWork.h b/modules/mpi/ospray/common/OSPWork.h index 66d35988de..fb219c1f72 100644 --- a/modules/mpi/ospray/common/OSPWork.h +++ b/modules/mpi/ospray/common/OSPWork.h @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once @@ -96,7 +96,7 @@ struct OSPState std::unordered_map framebuffers; - // Large data which is transfered separately from the command buffer, + // Large data which is transferred separately from the command buffer, // prior to sending the command buffer std::queue dataTransfers; diff --git a/modules/mpi/ospray/fb/DistributedFrameBuffer.cpp b/modules/mpi/ospray/fb/DistributedFrameBuffer.cpp index 117f3efbf5..a57af78cbc 100644 --- a/modules/mpi/ospray/fb/DistributedFrameBuffer.cpp +++ b/modules/mpi/ospray/fb/DistributedFrameBuffer.cpp @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "DistributedFrameBuffer.h" @@ -506,7 +506,7 @@ void DFB::incoming(const std::shared_ptr &message) std::lock_guard lock(mutex); if (!frameIsActive) { throw std::runtime_error( - "Somehow recieved a tile message when frame inactive!?"); + "Somehow received a tile message when frame inactive!?"); } } diff --git a/modules/mpi/ospray/fb/DistributedFrameBuffer.h b/modules/mpi/ospray/fb/DistributedFrameBuffer.h index c36fcaaf92..7174e3e3c5 100644 --- a/modules/mpi/ospray/fb/DistributedFrameBuffer.h +++ b/modules/mpi/ospray/fb/DistributedFrameBuffer.h @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once @@ -110,7 +110,7 @@ struct DistributedFrameBuffer : public mpi::messaging::MessageHandler, /*! this function gets called whenever one of our tiles is done writing/compositing/blending/etc; i.e., as soon as we know - that all the ingredient tile datas for that tile have been + that all the ingredient tile data for that tile have been received from the client(s) that generated them. By the time the tile gets called we do know that 'accum' field of the tile has been set; it is this function's job to make sure we diff --git a/modules/mpi/ospray/fb/DistributedFrameBuffer_TileMessages.h b/modules/mpi/ospray/fb/DistributedFrameBuffer_TileMessages.h index ea0952363f..0df5bb869f 100644 --- a/modules/mpi/ospray/fb/DistributedFrameBuffer_TileMessages.h +++ b/modules/mpi/ospray/fb/DistributedFrameBuffer_TileMessages.h @@ -1,4 +1,4 @@ -// Copyright 2009-2020 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once @@ -19,7 +19,7 @@ enum COMMANDTAG WORKER_WRITE_TILE = 1 << 1, /*! command tag used for sending 'final' tiles from the tile owner to the master frame buffer. Note that we *do* send a - message back ot the master even in cases where the master + message back to the master even in cases where the master does not actually care about the pixel data - we still have to let the master know when we're done. */ MASTER_WRITE_TILE_I8 = 1 << 2, diff --git a/modules/mpi/tutorials/ospMPIDistribTutorial.c b/modules/mpi/tutorials/ospMPIDistribTutorial.c index 917a86dcd4..cf89e5830d 100644 --- a/modules/mpi/tutorials/ospMPIDistribTutorial.c +++ b/modules/mpi/tutorials/ospMPIDistribTutorial.c @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 /* This is a small example tutorial how to use OSPRay and the @@ -190,7 +190,7 @@ int main(int argc, char **argv) ospCommit(world); - // create the mpi_raycast renderer (requred for distributed rendering) + // create the mpi_raycast renderer (required for distributed rendering) OSPRenderer renderer = ospNewRenderer("mpiRaycast"); // create and setup light for Ambient Occlusion diff --git a/modules/mpi/tutorials/ospMPIDistribTutorial.cpp b/modules/mpi/tutorials/ospMPIDistribTutorial.cpp index 2bca9e9282..350b414159 100644 --- a/modules/mpi/tutorials/ospMPIDistribTutorial.cpp +++ b/modules/mpi/tutorials/ospMPIDistribTutorial.cpp @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 /* This is a small example tutorial how to use OSPRay and the @@ -142,7 +142,7 @@ int main(int argc, char **argv) world.commit(); - // create the mpi_raycast renderer (requred for distributed rendering) + // create the mpi_raycast renderer (required for distributed rendering) cpp::Renderer renderer("mpiRaycast"); renderer.commit(); diff --git a/modules/mpi/tutorials/ospMPIDistribTutorialAsync.c b/modules/mpi/tutorials/ospMPIDistribTutorialAsync.c index e7f4d97aac..4514f0cceb 100644 --- a/modules/mpi/tutorials/ospMPIDistribTutorialAsync.c +++ b/modules/mpi/tutorials/ospMPIDistribTutorialAsync.c @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 /* This is a small example tutorial how to use OSPRay and the @@ -190,7 +190,7 @@ int main(int argc, char **argv) ospCommit(world); - // create the mpi_raycast renderer (requred for distributed rendering) + // create the mpi_raycast renderer (required for distributed rendering) OSPRenderer renderer = ospNewRenderer("mpiRaycast"); // create and setup light for Ambient Occlusion diff --git a/modules/multiDevice/MultiDeviceLoadBalancer.cpp b/modules/multiDevice/MultiDeviceLoadBalancer.cpp index 49224cdb45..7623da15a0 100644 --- a/modules/multiDevice/MultiDeviceLoadBalancer.cpp +++ b/modules/multiDevice/MultiDeviceLoadBalancer.cpp @@ -1,3 +1,6 @@ +// Copyright 2021-2022 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + #include "MultiDeviceLoadBalancer.h" #include "MultiDevice.h" #include "fb/LocalFB.h" @@ -134,7 +137,8 @@ void MultiDeviceLoadBalancer::renderTiles(api::MultiDeviceObject *framebuffer, OSPFrameBufferFormat colorBufferFormat = fb0->getColorBufferFormat(); tasking::parallel_for(loadBalancers.size(), [&](size_t subdeviceID) { - if (subdeviceID == 0) return; //gather of fb0's tiles to fb0 is unecessary + if (subdeviceID == 0) + return; // gather of fb0's tiles to fb0 is unnecessary FrameBuffer *fbi = (FrameBuffer *)framebuffer->objects[subdeviceID]; //get a hold of the pixels @@ -170,7 +174,7 @@ void MultiDeviceLoadBalancer::renderTiles(api::MultiDeviceObject *framebuffer, const vec2i tileID(tile_x, tile_y); const int32 accumID = 0;//fbi->accumID(tileID); this prevents re-accum across source and dest, which is good as dest's accum pixels are not valid (LocalFB.ispc:92) - #if TILE_SIZE > MAX_TILE_SIZE +#if TILE_SIZE > MAX_TILE_SIZE auto tilePtr = make_unique(tileID, fbSize, accumID); auto &tile = *tilePtr; #else @@ -318,7 +322,6 @@ void MultiDeviceLoadBalancer::renderTiles(api::MultiDeviceObject *framebuffer, fbi->unmap(depth); fbi->unmap(normal); fbi->unmap(albedo); - }); } diff --git a/ospray/api/Device.h b/ospray/api/Device.h index 405f12b573..7b98b59791 100644 --- a/ospray/api/Device.h +++ b/ospray/api/Device.h @@ -1,4 +1,4 @@ -// Copyright 2009-2020 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once @@ -195,7 +195,7 @@ OSPRAY_CORE_INTERFACE Device ¤tDevice(); OSPRAY_CORE_INTERFACE std::string generateEmbreeDeviceCfg(const Device &device); -// Inlined defintions ///////////////////////////////////////////////////////// +// Inlined definitions ///////////////////////////////////////////////////////// template inline void Device::registerType(const char *type) diff --git a/ospray/common/OSPCommon.cpp b/ospray/common/OSPCommon.cpp index 5f7cca6457..51fdccd14a 100644 --- a/ospray/common/OSPCommon.cpp +++ b/ospray/common/OSPCommon.cpp @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // must be first @@ -144,35 +144,35 @@ void initFromCommandLine(int *_ac, const char ***_av) } for (int i = 1; i < ac;) { - std::string parm = av[i]; + std::string param = av[i]; // flag-style arguments - if (parm == "--osp:debug") { + if (param == "--osp:debug") { device->setParam("debug", true); device->setParam("logOutput", std::string("cout")); device->setParam("errorOutput", std::string("cerr")); removeArgs(ac, av, i, 1); - } else if (parm == "--osp:warn-as-error") { + } else if (param == "--osp:warn-as-error") { device->setParam("warnAsError", true); removeArgs(ac, av, i, 1); - } else if (parm == "--osp:verbose") { + } else if (param == "--osp:verbose") { device->setParam("logLevel", OSP_LOG_INFO); device->setParam("logOutput", std::string("cout")); device->setParam("errorOutput", std::string("cerr")); removeArgs(ac, av, i, 1); - } else if (parm == "--osp:vv") { + } else if (param == "--osp:vv") { device->setParam("logLevel", OSP_LOG_DEBUG); device->setParam("logOutput", std::string("cout")); device->setParam("errorOutput", std::string("cerr")); removeArgs(ac, av, i, 1); } // arguments taking required values - else if (beginsWith(parm, "--osp:log-level")) { - std::string str = getArgString(parm); + else if (beginsWith(param, "--osp:log-level")) { + std::string str = getArgString(param); auto level = api::Device::logLevelFromString(str); device->setParam("logLevel", level.value_or(0)); removeArgs(ac, av, i, 1); - } else if (beginsWith(parm, "--osp:log-output")) { - std::string dst = getArgString(parm); + } else if (beginsWith(param, "--osp:log-output")) { + std::string dst = getArgString(param); if (dst == "cout" || dst == "cerr") { device->setParam("logOutput", dst); } else { @@ -182,8 +182,8 @@ void initFromCommandLine(int *_ac, const char ***_av) postStatusMsg(ss); } removeArgs(ac, av, i, 1); - } else if (beginsWith(parm, "--osp:error-output")) { - std::string dst = getArgString(parm); + } else if (beginsWith(param, "--osp:error-output")) { + std::string dst = getArgString(param); if (dst == "cout" || dst == "cerr") { device->setParam("errorOutput", dst); } else { @@ -193,12 +193,12 @@ void initFromCommandLine(int *_ac, const char ***_av) postStatusMsg(ss); } removeArgs(ac, av, i, 1); - } else if (beginsWith(parm, "--osp:num-threads")) { - int nt = std::max(1, getArgInt(parm)); + } else if (beginsWith(param, "--osp:num-threads")) { + int nt = std::max(1, getArgInt(param)); device->setParam("numThreads", nt); removeArgs(ac, av, i, 1); - } else if (beginsWith(parm, "--osp:set-affinity")) { - int val = getArgInt(parm); + } else if (beginsWith(param, "--osp:set-affinity")) { + int val = getArgInt(param); if (val == 0 || val == 1) { device->setParam("setAffinity", val); } else { diff --git a/ospray/include/ospray/ospray.h b/ospray/include/ospray/ospray.h index 3560cf0749..f0a1de8b25 100644 --- a/ospray/include/ospray/ospray.h +++ b/ospray/include/ospray/ospray.h @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once @@ -43,7 +43,7 @@ #define OSP_DEFAULT_VAL(a) #endif -// Give OSPRay handle types a concrete defintion to enable C++ type checking +// Give OSPRay handle types a concrete definition to enable C++ type checking #ifdef __cplusplus namespace osp { struct Device; diff --git a/ospray/include/ospray/ospray_cpp/Data.h b/ospray/include/ospray/ospray_cpp/Data.h index ba3bd1a9de..8ed4e862d9 100644 --- a/ospray/include/ospray/ospray_cpp/Data.h +++ b/ospray/include/ospray/ospray_cpp/Data.h @@ -1,4 +1,4 @@ -// Copyright 2009-2020 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once @@ -184,7 +184,7 @@ inline void Data::validate_element_type() static_assert(OSPTypeFor::value != OSP_UNKNOWN, "Only types corresponding to OSPDataType values can be set " "as elements in OSPRay Data arrays. NOTE: Math types (vec, " - "box, linear, affine) must be inferrable as OSP_* enums, which are " + "box, linear, affine) must be inferable as OSP_* enums, which are " "defined by the OSPTYPEFOR_SPECIALIZATION() macro."); } diff --git a/scripts/superbuild/README.md b/scripts/superbuild/README.md index b21656c050..4f756c1e4c 100644 --- a/scripts/superbuild/README.md +++ b/scripts/superbuild/README.md @@ -21,6 +21,6 @@ CMake options to note (all have sensible defaults): - `BUILD_JOBS` sets the number given to `make -j` for parallel builds. - `BUILD_EMBREE_FROM_SOURCE` set to OFF will download a pre-built version of Embree. - `BUILD_OIDN_FROM_SOURCE` set to OFF will download a pre-built version of OpenImageDenoise. -- `BUILD_OIDN_VERSION` determines which verison of OpenImageDenoise to pull down. +- `BUILD_OIDN_VERSION` determines which version of OpenImageDenoise to pull down. - `BUILD_TBB_FROM_SOURCE` set to ON to build TBB from source (required for ARM support). The default setting is OFF. diff --git a/scripts/tests/run_tests.ps1 b/scripts/tests/run_tests.ps1 index ea1a2ce3b9..879c0dc95f 100755 --- a/scripts/tests/run_tests.ps1 +++ b/scripts/tests/run_tests.ps1 @@ -1,4 +1,4 @@ -## Copyright 2009-2021 Intel Corporation +## Copyright 2009-2022 Intel Corporation ## SPDX-License-Identifier: Apache-2.0 # to run: ./run_tests.ps1 [TEST_MPI] @@ -43,7 +43,7 @@ if ( $testMPI ) { exit $exitCode } - # TODO: reenable tests failing with MSVC15 + # TODO: re-enable tests failing with MSVC15 md failed-mpi-data-parallel #mpiexec.exe -n 2 ospMPIDistribTestSuite.exe --gtest_output=xml:tests-mpi.xml --baseline-dir=regression_test_baseline\ --failed-dir=failed-mpi-data-parallel $exitCode = $LastExitCode From 1549c1b32c309078ac96e007f01aa4533bbaae53 Mon Sep 17 00:00:00 2001 From: Will Usher Date: Tue, 18 Jan 2022 20:05:28 -0800 Subject: [PATCH 17/26] Resolve comm dup crash due to leaking FBs, obj handle less error prone Refactor ObjectHandle creation and use to be less error prone Also add handle validation checking in distributed device for debugging --- CHANGELOG.md | 4 ++ modules/mpi/common/ObjectHandle.cpp | 26 +++++----- modules/mpi/common/ObjectHandle.h | 9 ++-- modules/mpi/ospray/MPIDistributedDevice.cpp | 47 +++++++++++++++---- modules/mpi/ospray/MPIDistributedDevice.h | 4 +- modules/mpi/ospray/MPIOffloadDevice.cpp | 4 +- modules/mpi/ospray/common/OSPWork.cpp | 13 +---- .../mpi/ospray/fb/DistributedFrameBuffer.cpp | 3 +- 8 files changed, 72 insertions(+), 38 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bdc611099a..ac9c52e879 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,10 @@ Version History - Changed from area sampling to solid angle sampling for quad lights in the pathtracer - Instances can now have the group object rebound via a parameter +- MPI: Resolve issue where framebuffers would be leaked in the MPI + offload device. +- MPI: Resolve possible race condition in assigning IDs to distributed + objects ### Changes in v2.8.0: diff --git a/modules/mpi/common/ObjectHandle.cpp b/modules/mpi/common/ObjectHandle.cpp index 7da861a55e..a31886ee94 100644 --- a/modules/mpi/common/ObjectHandle.cpp +++ b/modules/mpi/common/ObjectHandle.cpp @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "ObjectHandle.h" @@ -21,16 +21,7 @@ void ObjectHandle::free() freedHandles.push((int64) * this); } -ObjectHandle::ObjectHandle() -{ - if (freedHandles.empty()) { - i32.ID = nextFreeLocalID++; - i32.owner = 0; - } else { - i64 = freedHandles.top(); - freedHandles.pop(); - } -} +ObjectHandle::ObjectHandle() : i64(NULL_HANDLE) {} ObjectHandle::ObjectHandle(int64 i) : i64(i) {} @@ -82,6 +73,19 @@ bool ObjectHandle::defined() const return objectByHandle.find(i64) != objectByHandle.end(); } +ObjectHandle ObjectHandle::allocateLocalHandle() +{ + ObjectHandle handle; + if (freedHandles.empty()) { + handle.i32.ID = nextFreeLocalID++; + handle.i32.owner = 0; + } else { + handle.i64 = freedHandles.top(); + freedHandles.pop(); + } + return handle; +} + ManagedObject *ObjectHandle::lookup() const { if (i64 == 0) diff --git a/modules/mpi/common/ObjectHandle.h b/modules/mpi/common/ObjectHandle.h index c170bd5c29..706585d840 100644 --- a/modules/mpi/common/ObjectHandle.h +++ b/modules/mpi/common/ObjectHandle.h @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once @@ -26,16 +26,19 @@ struct ManagedObject; */ union ObjectHandle { - void free(); - ObjectHandle(); ObjectHandle(int64 i); ObjectHandle(const ObjectHandle &other); ObjectHandle &operator=(const ObjectHandle &other); + void free(); + /*! look up an object by handle, and return it. must be a defined handle */ ManagedObject *lookup() const; + /* Allocate a local ObjectHandle */ + static ObjectHandle allocateLocalHandle(); + /*! Return the handle associated with the given object. */ static ObjectHandle lookup(ManagedObject *object); diff --git a/modules/mpi/ospray/MPIDistributedDevice.cpp b/modules/mpi/ospray/MPIDistributedDevice.cpp index ea1fa1c688..dd25388ce2 100644 --- a/modules/mpi/ospray/MPIDistributedDevice.cpp +++ b/modules/mpi/ospray/MPIDistributedDevice.cpp @@ -1,10 +1,10 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "MPIDistributedDevice.h" #include -#include "MPIDistributedDevice_ispc.h" #include "ISPCDevice.h" +#include "MPIDistributedDevice_ispc.h" #include "camera/Camera.h" #include "common/Data.h" #include "common/DistributedWorld.h" @@ -157,13 +157,10 @@ inline OSPVolume createLocalObject(const char *type) } template -inline API_TYPE createDistributedObject(const char *type) +inline API_TYPE createDistributedObject(const char *type, ObjectHandle handle) { auto *instance = OSPRAY_TYPE::createInstance(type); - - ObjectHandle handle; handle.assign(instance); - return (API_TYPE)(int64)handle; } @@ -300,7 +297,7 @@ void MPIDistributedDevice::commit() OSPFrameBuffer MPIDistributedDevice::frameBufferCreate( const vec2i &size, const OSPFrameBufferFormat mode, const uint32 channels) { - ObjectHandle handle; + ObjectHandle handle = allocateHandle(); auto *instance = new DistributedFrameBuffer(size, handle, mode, channels); handle.assign(instance); return (OSPFrameBuffer)(int64)handle; @@ -346,7 +343,7 @@ OSPInstance MPIDistributedDevice::newInstance(OSPGroup _group) OSPWorld MPIDistributedDevice::newWorld() { - ObjectHandle handle; + ObjectHandle handle = allocateHandle(); auto *instance = new DistributedWorld; instance->setDevice(embreeDevice); handle.assign(instance); @@ -391,7 +388,8 @@ OSPImageOperation MPIDistributedDevice::newImageOp(const char *type) OSPRenderer MPIDistributedDevice::newRenderer(const char *type) { - return createDistributedObject(type); + ObjectHandle handle = allocateHandle(); + return createDistributedObject(type, handle); } OSPCamera MPIDistributedDevice::newCamera(const char *type) @@ -602,5 +600,36 @@ OSPPickResult MPIDistributedDevice::pick(OSPFrameBuffer _fb, return renderer->pick(fb, camera, world, screenPos); } +ObjectHandle MPIDistributedDevice::allocateHandle() +{ + mpicommon::barrier(mpicommon::worker.comm).wait(); + ObjectHandle handle = ObjectHandle::allocateLocalHandle(); + + // For debugging check that all ranks did in fact allocate the same handle. + // Typically we assume this is the case, as the app should be creating + // distributed objects in lock-step, even if their local objects differ. + if (logLevel == OSP_LOG_DEBUG) { + int maxID = handle.i32.ID; + int minID = handle.i32.ID; + + auto reduceMax = mpicommon::reduce( + &handle.i32.ID, &maxID, 1, MPI_INT, MPI_MAX, 0, mpicommon::worker.comm); + auto reduceMin = mpicommon::reduce( + &handle.i32.ID, &minID, 1, MPI_INT, MPI_MIN, 0, mpicommon::worker.comm); + reduceMax.wait(); + reduceMin.wait(); + + if (maxID != minID) { + // Log it, but this is a fatal error + postStatusMsg( + "Error allocating distributed handles: Ranks do not all have the same handle!"); + throw std::runtime_error( + "Error allocating distributed handles: Ranks do not all have the same handle!"); + } + } + + return handle; +} + } // namespace mpi } // namespace ospray diff --git a/modules/mpi/ospray/MPIDistributedDevice.h b/modules/mpi/ospray/MPIDistributedDevice.h index 357b1efd45..928441c89b 100644 --- a/modules/mpi/ospray/MPIDistributedDevice.h +++ b/modules/mpi/ospray/MPIDistributedDevice.h @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once @@ -152,6 +152,8 @@ struct MPIDistributedDevice : public api::Device OSPFrameBuffer, OSPRenderer, OSPCamera, OSPWorld, const vec2f &) override; private: + ObjectHandle allocateHandle(); + bool initialized{false}; bool shouldFinalizeMPI{false}; diff --git a/modules/mpi/ospray/MPIOffloadDevice.cpp b/modules/mpi/ospray/MPIOffloadDevice.cpp index 7acf4f3c63..8c6cc16136 100644 --- a/modules/mpi/ospray/MPIOffloadDevice.cpp +++ b/modules/mpi/ospray/MPIOffloadDevice.cpp @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #ifndef _WIN32 @@ -1148,7 +1148,7 @@ int MPIOffloadDevice::rootWorkerRank() const ObjectHandle MPIOffloadDevice::allocateHandle() const { - return ObjectHandle(); + return ObjectHandle::allocateLocalHandle(); } } // namespace mpi diff --git a/modules/mpi/ospray/common/OSPWork.cpp b/modules/mpi/ospray/common/OSPWork.cpp index f6c30ab943..055b087851 100644 --- a/modules/mpi/ospray/common/OSPWork.cpp +++ b/modules/mpi/ospray/common/OSPWork.cpp @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include @@ -340,13 +340,7 @@ void release( if (fnd != state.framebuffers.end()) { OSPObject obj = state.objects[handle]; ManagedObject *m = lookupDistributedObject(obj); - // Framebuffers are given an extra ref count by the worker so that - // we can track the lifetime of their framebuffer info. Use count == 1 - // means only the worker rank has a reference to the object - if (m->useCount() == 1) { - ospRelease(state.objects[handle]); - state.framebuffers.erase(fnd); - } + state.framebuffers.erase(fnd); } } @@ -383,9 +377,6 @@ void createFramebuffer( ospNewFrameBuffer(size.x, size.y, (OSPFrameBufferFormat)format, channels); state.framebuffers[handle] = FrameBufferInfo(size, (OSPFrameBufferFormat)format, channels); - - // Offload device keeps +1 ref for tracking the lifetime of the framebuffer - ospRetain(state.objects[handle]); } void mapFramebuffer(OSPState &state, diff --git a/modules/mpi/ospray/fb/DistributedFrameBuffer.cpp b/modules/mpi/ospray/fb/DistributedFrameBuffer.cpp index a57af78cbc..2ae5adc1a6 100644 --- a/modules/mpi/ospray/fb/DistributedFrameBuffer.cpp +++ b/modules/mpi/ospray/fb/DistributedFrameBuffer.cpp @@ -37,7 +37,8 @@ void DistributedTileError::sync() if (tiles <= 0) return; - mpicommon::bcast(tileErrorBuffer.data(), tiles, MPI_FLOAT, 0, group.comm).wait(); + mpicommon::bcast(tileErrorBuffer.data(), tiles, MPI_FLOAT, 0, group.comm) + .wait(); } // DistributedFrameBuffer definitions /////////////////////////////////////// From 5c4c3f8232fd365d804656066577027e792dca34 Mon Sep 17 00:00:00 2001 From: Will Usher Date: Tue, 25 Jan 2022 14:21:46 -0800 Subject: [PATCH 18/26] Add support for Intel oneAPI DPCPP compiler --- cmake/compiler/dpcpp.cmake | 62 ++++++++++++++++++++++++++++++++++++++ cmake/ospray_macros.cmake | 6 +++- 2 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 cmake/compiler/dpcpp.cmake diff --git a/cmake/compiler/dpcpp.cmake b/cmake/compiler/dpcpp.cmake new file mode 100644 index 0000000000..ba514c0d75 --- /dev/null +++ b/cmake/compiler/dpcpp.cmake @@ -0,0 +1,62 @@ +## Copyright 2009-2022 Intel Corporation +## SPDX-License-Identifier: Apache-2.0 + +set(OSPRAY_CXX_FLAGS "-fno-strict-aliasing -Wno-narrowing") + +if(OSPRAY_STRICT_BUILD) + # OK to turn off. + set(OSPRAY_CXX_FLAGS "-Wno-c++98-compat-pedantic ${OSPRAY_CXX_FLAGS}") + set(OSPRAY_CXX_FLAGS "-Wno-documentation ${OSPRAY_CXX_FLAGS}") + set(OSPRAY_CXX_FLAGS "-Wno-documentation-unknown-command ${OSPRAY_CXX_FLAGS}") + set(OSPRAY_CXX_FLAGS "-Wno-zero-as-null-pointer-constant ${OSPRAY_CXX_FLAGS}") + set(OSPRAY_CXX_FLAGS "-Wno-newline-eof ${OSPRAY_CXX_FLAGS}") + set(OSPRAY_CXX_FLAGS "-Wno-keyword-macro ${OSPRAY_CXX_FLAGS}") #useful for unit testing + set(OSPRAY_CXX_FLAGS "-Wno-undef ${OSPRAY_CXX_FLAGS}") + set(OSPRAY_CXX_FLAGS "-Wno-header-hygiene ${OSPRAY_CXX_FLAGS}") + set(OSPRAY_CXX_FLAGS "-Wno-covered-switch-default ${OSPRAY_CXX_FLAGS}") + set(OSPRAY_CXX_FLAGS "-Wno-date-time ${OSPRAY_CXX_FLAGS}") + + # Should try to fix and remove... + set(OSPRAY_CXX_FLAGS "-Wno-unknown-warning-option ${OSPRAY_CXX_FLAGS}") #don't warn if pragmas are unknown + set(OSPRAY_CXX_FLAGS "-Wno-conversion ${OSPRAY_CXX_FLAGS}") + set(OSPRAY_CXX_FLAGS "-Wno-reserved-id-macro ${OSPRAY_CXX_FLAGS}") + set(OSPRAY_CXX_FLAGS "-Wno-double-promotion ${OSPRAY_CXX_FLAGS}") + set(OSPRAY_CXX_FLAGS "-Wno-used-but-marked-unused ${OSPRAY_CXX_FLAGS}") + set(OSPRAY_CXX_FLAGS "-Wno-old-style-cast ${OSPRAY_CXX_FLAGS}") + set(OSPRAY_CXX_FLAGS "-Wno-missing-noreturn ${OSPRAY_CXX_FLAGS}") + set(OSPRAY_CXX_FLAGS "-Wno-missing-prototypes ${OSPRAY_CXX_FLAGS}") + set(OSPRAY_CXX_FLAGS "-Wno-shift-sign-overflow ${OSPRAY_CXX_FLAGS}") + set(OSPRAY_CXX_FLAGS "-Wno-padded ${OSPRAY_CXX_FLAGS}") + set(OSPRAY_CXX_FLAGS "-Wno-shadow-field-in-constructor ${OSPRAY_CXX_FLAGS}") + set(OSPRAY_CXX_FLAGS "-Wno-weak-template-vtables ${OSPRAY_CXX_FLAGS}") + set(OSPRAY_CXX_FLAGS "-Wno-weak-vtables ${OSPRAY_CXX_FLAGS}") + set(OSPRAY_CXX_FLAGS "-Wno-exit-time-destructors ${OSPRAY_CXX_FLAGS}") + set(OSPRAY_CXX_FLAGS "-Wno-global-constructors ${OSPRAY_CXX_FLAGS}") + set(OSPRAY_CXX_FLAGS "-Wno-unused-template ${OSPRAY_CXX_FLAGS}") + set(OSPRAY_CXX_FLAGS "-Wno-switch-enum ${OSPRAY_CXX_FLAGS}") + set(OSPRAY_CXX_FLAGS "-Wno-float-equal ${OSPRAY_CXX_FLAGS}") + set(OSPRAY_CXX_FLAGS "-Wno-cast-align ${OSPRAY_CXX_FLAGS}") + set(OSPRAY_CXX_FLAGS "-Wno-deprecated ${OSPRAY_CXX_FLAGS}") + set(OSPRAY_CXX_FLAGS "-Wno-mismatched-tags ${OSPRAY_CXX_FLAGS}") + set(OSPRAY_CXX_FLAGS "-Wno-disabled-macro-expansion ${OSPRAY_CXX_FLAGS}") #pesky 'stb_image.h'... + set(OSPRAY_CXX_FLAGS "-Wno-over-aligned ${OSPRAY_CXX_FLAGS}") + set(OSPRAY_CXX_FLAGS "-Wno-shadow ${OSPRAY_CXX_FLAGS}") + set(OSPRAY_CXX_FLAGS "-Wno-format-nonliteral ${OSPRAY_CXX_FLAGS}") + set(OSPRAY_CXX_FLAGS "-Wno-cast-qual ${OSPRAY_CXX_FLAGS}") #Embree v3.x issue + set(OSPRAY_CXX_FLAGS "-Wno-extra-semi-stmt ${OSPRAY_CXX_FLAGS}") + set(OSPRAY_CXX_FLAGS "-Wno-shadow-field ${OSPRAY_CXX_FLAGS}") + set(OSPRAY_CXX_FLAGS "-Wno-alloca ${OSPRAY_CXX_FLAGS}") + + # Options selected for Clang 5.0+ + set(OSPRAY_CXX_FLAGS "-Weverything ${OSPRAY_CXX_FLAGS}") +endif() + +if(OSPRAY_WARN_AS_ERRORS) + set(OSPRAY_CXX_FLAGS "-Werror ${OSPRAY_CXX_FLAGS}") +endif() + +set(CMAKE_CXX_FLAGS "${OSPRAY_CXX_FLAGS} ${CMAKE_CXX_FLAGS}") + +if (APPLE) + set(CMAKE_CXX_FLAGS "-mmacosx-version-min=10.9 ${CMAKE_CXX_FLAGS}") # we only use MacOSX 10.9 features +endif() diff --git a/cmake/ospray_macros.cmake b/cmake/ospray_macros.cmake index 935b53f03f..a1a8800e2e 100644 --- a/cmake/ospray_macros.cmake +++ b/cmake/ospray_macros.cmake @@ -1,4 +1,4 @@ -## Copyright 2009-2021 Intel Corporation +## Copyright 2009-2022 Intel Corporation ## SPDX-License-Identifier: Apache-2.0 include(CMakeFindDependencyMacro) @@ -316,6 +316,7 @@ macro(ospray_configure_compiler) set(OSPRAY_COMPILER_GCC FALSE) set(OSPRAY_COMPILER_CLANG FALSE) set(OSPRAY_COMPILER_MSVC FALSE) + set(OSPRAY_COMPILER_DPCPP FALSE) if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel") set(OSPRAY_COMPILER_ICC TRUE) @@ -334,6 +335,9 @@ macro(ospray_configure_compiler) elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") set(OSPRAY_COMPILER_MSVC TRUE) include(msvc) + elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "IntelLLVM") + set(OSPRAY_COMPILER_DPCPP TRUE) + include(dpcpp) else() message(FATAL_ERROR "Unsupported compiler specified: '${CMAKE_CXX_COMPILER_ID}'") From 23185f67f617bd204fdefc8a4a9114f8fb451a00 Mon Sep 17 00:00:00 2001 From: Roba Binyahib Date: Mon, 31 Jan 2022 11:01:42 +0100 Subject: [PATCH 19/26] adding support for dynamic load balancing in Offload device init DynamicLoadBalancer outline sending half of workload to thief sending half of received workload to stored lifelines adding functions to the Dynamic Load Balancer class to handle the work queue adding structs for the different messages creating ObjectHandle at the renderFrame API call passing the loadBalancer to ThreadedRenderTask enabling static balancer through environment variable threading sync --- CHANGELOG.md | 3 +- doc/api.md | 4 + modules/mpi/common/Collectives.h | 2 +- modules/mpi/ospray/CMakeLists.txt | 3 +- modules/mpi/ospray/MPIDistributedDevice.cpp | 11 +- modules/mpi/ospray/MPIDistributedDevice.h | 3 +- .../mpi/ospray/common/DynamicLoadBalancer.cpp | 322 ++++++++++++++++++ .../mpi/ospray/common/DynamicLoadBalancer.h | 138 ++++++++ .../mpi/ospray/fb/DistributedFrameBuffer.cpp | 4 + .../mpi/ospray/fb/DistributedFrameBuffer.h | 1 + .../ospray/render/DistributedLoadBalancer.cpp | 126 +++++-- .../ospray/render/DistributedLoadBalancer.h | 10 +- .../mpi/ospray/render/ThreadedRenderTask.h | 16 +- 13 files changed, 605 insertions(+), 38 deletions(-) create mode 100644 modules/mpi/ospray/common/DynamicLoadBalancer.cpp create mode 100644 modules/mpi/ospray/common/DynamicLoadBalancer.h diff --git a/CHANGELOG.md b/CHANGELOG.md index ac9c52e879..f2a9abf439 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ Version History --------------- ### Changes in v2.9.0: - +- Add support for dynamic load balancing in Offload device - Add support for multi-segment deformation motion blur for `mesh` geometry - Moved ISPC module to `modules/` folder and renamed the module to @@ -1148,4 +1148,3 @@ changes. - Corrected memory management for shared data buffers - Updated to ISPC 1.8.1 - Resolved issue in XML parser - diff --git a/doc/api.md b/doc/api.md index 2578562a0c..0ed2d34993 100644 --- a/doc/api.md +++ b/doc/api.md @@ -3201,6 +3201,10 @@ The `maxCommandBufferEntries`, `commandBufferSize`, and `OSPRAY_MPI_COMMAND_BUFFER_SIZE`, and `OSPRAY_MPI_MAX_INLINE_DATA_SIZE`, respectively. +The `mpiOffload` device uses a dynamic load balancer by default. If you +wish to use a static load balancer you can do so by setting the +`OSPRAY_STATIC_BALANCER` environment variable to 1. + The `mpiOffload` device does not support multiple init/shutdown cycles. Thus, to run `ospBenchmark` for this device make sure to exclude the init/shutdown test by passing `--benchmark_filter=-ospInit_ospShutdown` diff --git a/modules/mpi/common/Collectives.h b/modules/mpi/common/Collectives.h index c9765afaa7..27156a0d33 100644 --- a/modules/mpi/common/Collectives.h +++ b/modules/mpi/common/Collectives.h @@ -92,7 +92,7 @@ std::future allreduce(const void *sendBuffer, std::future send(void *buffer, int count, MPI_Datatype datatype, - int source, + int destination, int tag, MPI_Comm comm); diff --git a/modules/mpi/ospray/CMakeLists.txt b/modules/mpi/ospray/CMakeLists.txt index 4932d6da30..fd0ec4d552 100644 --- a/modules/mpi/ospray/CMakeLists.txt +++ b/modules/mpi/ospray/CMakeLists.txt @@ -1,4 +1,4 @@ -## Copyright 2009-2021 Intel Corporation +## Copyright 2009-2022 Intel Corporation ## SPDX-License-Identifier: Apache-2.0 ## Options ## @@ -33,6 +33,7 @@ ispc_target_add_sources(ospray_module_mpi common/OSPWork.cpp common/Messaging.cpp + common/DynamicLoadBalancer.cpp fb/DistributedFrameBuffer.cpp fb/DistributedFrameBuffer.ispc diff --git a/modules/mpi/ospray/MPIDistributedDevice.cpp b/modules/mpi/ospray/MPIDistributedDevice.cpp index dd25388ce2..6207f0a845 100644 --- a/modules/mpi/ospray/MPIDistributedDevice.cpp +++ b/modules/mpi/ospray/MPIDistributedDevice.cpp @@ -180,9 +180,7 @@ static void vklErrorFunc(void *, const VKLError code, const char *str) // MPIDistributedDevice definitions /////////////////////////////////////// -MPIDistributedDevice::MPIDistributedDevice() - : loadBalancer(std::make_shared()) -{} +MPIDistributedDevice::MPIDistributedDevice() {} MPIDistributedDevice::~MPIDistributedDevice() { @@ -458,6 +456,11 @@ OSPFuture MPIDistributedDevice::renderFrame(OSPFrameBuffer _fb, auto *camera = lookupObject(_camera); auto *world = lookupObject(_world); + ObjectHandle handle = allocateHandle(); + std::shared_ptr loadBalancer = + std::make_shared(); + loadBalancer->setObjectHandle(handle); + fb->setCompletedEvent(OSP_NONE_FINISHED); fb->refInc(); @@ -465,7 +468,7 @@ OSPFuture MPIDistributedDevice::renderFrame(OSPFrameBuffer _fb, camera->refInc(); world->refInc(); - auto *f = new ThreadedRenderTask(fb, [=]() { + auto *f = new ThreadedRenderTask(fb, loadBalancer, [=]() { #ifdef ENABLE_PROFILING using namespace mpicommon; ProfilingPoint start; diff --git a/modules/mpi/ospray/MPIDistributedDevice.h b/modules/mpi/ospray/MPIDistributedDevice.h index 928441c89b..4596641719 100644 --- a/modules/mpi/ospray/MPIDistributedDevice.h +++ b/modules/mpi/ospray/MPIDistributedDevice.h @@ -8,6 +8,7 @@ #include "common/Managed.h" #include "common/ObjectHandle.h" #include "openvkl/openvkl.h" +#include "render/DistributedLoadBalancer.h" #include "render/LoadBalancer.h" namespace ospray { @@ -157,8 +158,6 @@ struct MPIDistributedDevice : public api::Device bool initialized{false}; bool shouldFinalizeMPI{false}; - std::shared_ptr loadBalancer; - RTCDevice embreeDevice = nullptr; VKLDevice vklDevice = nullptr; }; diff --git a/modules/mpi/ospray/common/DynamicLoadBalancer.cpp b/modules/mpi/ospray/common/DynamicLoadBalancer.cpp new file mode 100644 index 0000000000..e388d0e8f8 --- /dev/null +++ b/modules/mpi/ospray/common/DynamicLoadBalancer.cpp @@ -0,0 +1,322 @@ +// Copyright 2009-2022 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +// This code is for DynamicLoadBalancer using the lifeline scheduling method + +#include "DynamicLoadBalancer.h" +#include +#include "Messaging.h" +#include "common/Profiling.h" +#include "rkcommon/utility/ArrayView.h" + +namespace ospray { +namespace mpi { +using namespace mpicommon; + +DynamicLoadBalancer::DynamicLoadBalancer(ObjectHandle myId, int numTasks) + : MessageHandler(myId) +{ + activeTasks = numTasks; +} +void DynamicLoadBalancer::setActiveTasks(int numTasks) +{ + std::lock_guard guard(termMutex); + activeTasks = numTasks; +} + +void DynamicLoadBalancer::updateActiveTasks(int term) +{ + std::lock_guard guard(termMutex); + activeTasks = activeTasks - term; +} +int DynamicLoadBalancer::getActiveTasks() +{ + int updatedActiveTasks = 0; + { + std::lock_guard guard(termMutex); + updatedActiveTasks = activeTasks; + } + return updatedActiveTasks; +} +void DynamicLoadBalancer::addWork(Work workItem) +{ + std::lock_guard guard(workMutex); + workQueue.push_back(workItem); +} + +int DynamicLoadBalancer::getWorkSize() +{ + int workSize; + { + std::lock_guard guard(workMutex); + workSize = workQueue.size(); + } + return workSize; +} + +Work DynamicLoadBalancer::getWorkItemFront() +{ + Work workItem; + std::lock_guard guard(workMutex); + workItem.ntasks = workQueue[0].ntasks; + workItem.offset = workQueue[0].offset; + workItem.ownerRank = workQueue[0].ownerRank; + workQueue.pop_front(); + + return workItem; +} + +Work DynamicLoadBalancer::getWorkItemBack() +{ + std::lock_guard guard(workMutex); + Work workItem = workQueue.back(); + workQueue.pop_back(); + + return workItem; +} +// This function takes the rank id and convert it into coords based on the base +// we are using to represent the ranks +std::vector DynamicLoadBalancer::rankToCoords( + int rank, unsigned int base, unsigned int power) +{ + std::vector result; + if (rank >= pow(base, power)) { + postStatusMsg(OSP_LOG_DEBUG) << "ERROR: Requested rank (" << rank + << ") out of bond. Increase base or power.\n"; + return result; + } + + result.resize(power); + int left = rank; + for (unsigned int i = 0; i < power; i++) { + result[i] = left / pow(base, power - i - 1); + left -= result[i] * pow(base, power - i - 1); + } + + return result; +} + +// -------------------------------------------------------------------- +// This function Takes coords of a rank and returns the rank id +unsigned int DynamicLoadBalancer::coordsToRank(std::vector &coords, + unsigned int base, + unsigned int power, + int numRanks) +{ + if (coords.size() != power) { + postStatusMsg(OSP_LOG_DEBUG) + << "ERROR: Coords do not match the requested dimensions.\n"; + return -1; + } + + unsigned int result = 0; + for (unsigned int i = 0; i < power; i++) + result += coords[i] * pow(base, power - i - 1); + + if (numRanks <= result) + result = result - numRanks; + if (result < 0) + result = 0; + + return result; +} + +// -------------------------------------------------------------------- +// This function takes the rank coords and compute the lifelines IDs +std::vector DynamicLoadBalancer::getLifelineID( + std::vector &rankCoords, + unsigned base, + unsigned int power, + int numRanks) +{ + std::vector result; + if (rankCoords.size() != power) { + return result; + } + + int myLifeLineID; + std::vector lifelineCoords(power); + + // loop over rank lifelines + for (unsigned int i = 0; i < power; i++) { + // loop over coords of lifeline i + for (unsigned int j = 0; j < power; j++) { + if (i == j) { + if (base - 1 <= rankCoords[i]) + lifelineCoords[j] = 0; + else + lifelineCoords[j] = rankCoords[i] + 1; + + } else + lifelineCoords[j] = rankCoords[j]; + } + + myLifeLineID = coordsToRank(lifelineCoords, base, power, numRanks); + if (-1 < myLifeLineID) + result.push_back(myLifeLineID); + } + + return result; +} + +// This function compute the lifelines of the passed rank +std::vector DynamicLoadBalancer::getMyLifeLines( + int rank, int numRanks, unsigned int base, unsigned int power) +{ + std::vector ranksCoords = rankToCoords(rank, base, power); + std::vector lifelines = + getLifelineID(ranksCoords, base, power, numRanks); + + return lifelines; +} + +// -------------------------------------------------------------------- +void DynamicLoadBalancer::incoming( + const std::shared_ptr &message) +{ + auto *header = (DynamicLBMessage *)message->data; + + if (header->type == TERMINATED) { + int numTerm = ((DynamicLBTerminatedMessage *)message->data)->numTerm; + updateActiveTasks(numTerm); + } // TERMINATED + else if (header->type == NEED_WORK) { + sendWork(header->senderRank); + } // NEED_WORK + else if (header->type == RECV_WORK) { + auto *workMsg = (DynamicLBSendWorkMessage *)message->data; + int numRecvWork = workMsg->numWorkItems; + const int recvBuffSize = + sizeof(DynamicLBSendWorkMessage) + numRecvWork * sizeof(Work); + auto *workItems = + (Work *)(message->data + sizeof(DynamicLBSendWorkMessage)); + Work myWork; + int senderRank = header->senderRank; + int workSize; + for (int i = 0; i < numRecvWork; i++) { + myWork.ntasks = workItems[i].ntasks; + myWork.offset = workItems[i].offset; + myWork.ownerRank = workItems[i].ownerRank; + addWork(myWork); + } + workSize = getWorkSize(); + if (1 < workSize) { + sendMultiWork(); + } + } // RECV_WORK + else { + postStatusMsg(OSP_LOG_ERROR) + << "Rank " << workerRank() << " Recv Unknown message\n"; + } +} + +// -------------------------------------------------------------------- +void DynamicLoadBalancer::requestWork() +{ + const int msgSize = sizeof(DynamicLBMessage); + + int base, power; + base = 2; + float p = log2(workerSize()); + if (pow(base, p) - workerSize() == 0) + power = p; + else + power = int(p) + 1; + + int victimID = 0; + myLifelines = getMyLifeLines(workerRank(), workerSize(), base, power); + + for (int i = 0; i < myLifelines.size(); i++) { + if (myLifelines[i] == workerRank()) + continue; + else { + auto msg = std::make_shared(msgSize); + auto *header = reinterpret_cast(msg->data); + header->type = NEED_WORK; + header->senderRank = workerRank(); + mpi::messaging::sendTo(myLifelines[i], myId, msg); + } + } +} +// -------------------------------------------------------------------- +void DynamicLoadBalancer::sendMultiWork() +{ + std::lock_guard lockThieves(thiefMutex); + if (thiefIds.empty()) { + return; + } + + // Try to steal as much work as we can for the thieves, while leaving some for + // this rank too + std::vector stolenWork = getWorkItems([&](size_t workSize) { + // +1 here to pretend that this rank is also a thief, so that we leave some + // work for it in the queue + const size_t workPerRank = workSize / (thiefIds.size() + 1); + return workPerRank * thiefIds.size(); + }); + if (stolenWork.empty()) { + return; + } + + // Each thief will be sent workPerRank work items + const size_t workPerThief = stolenWork.size() / thiefIds.size(); + size_t nextWorkSet = 0; + for (auto &thief : thiefIds) { + sendWorkToThief(thief, stolenWork.data() + nextWorkSet, workPerThief); + nextWorkSet += workPerThief; + } + thiefIds.clear(); +} +// -------------------------------------------------------------------- +void DynamicLoadBalancer::sendWork(int thiefID) +{ + if (thiefID != -1 && thiefID != workerRank()) { + std::vector stolenWork = + getWorkItems([&](size_t workSize) { return workSize / 2; }); + if (!stolenWork.empty()) { + sendWorkToThief(thiefID, stolenWork.data(), stolenWork.size()); + } else { + std::lock_guard lockThieves(thiefMutex); + thiefIds.insert(thiefID); + } + } +} + +void DynamicLoadBalancer::sendWorkToThief( + int thiefID, const Work *toSend, const size_t numWorkItems) +{ + const int msgSize = + sizeof(DynamicLBSendWorkMessage) + numWorkItems * sizeof(Work); + auto msg = std::make_shared(msgSize); + auto *header = reinterpret_cast(msg->data); + header->type = RECV_WORK; + header->senderRank = workerRank(); + header->numWorkItems = numWorkItems; + auto *workItems = + reinterpret_cast(msg->data + sizeof(DynamicLBSendWorkMessage)); + std::memcpy(workItems, toSend, numWorkItems * sizeof(Work)); + + mpi::messaging::sendTo(thiefID, myId, msg); +} + +// -------------------------------------------------------------------- +void DynamicLoadBalancer::sendTerm(int term) +{ + const int msgSize = sizeof(DynamicLBTerminatedMessage); + + if (0 < term) { + for (int i = 0; i < workerSize(); i++) + if (i != workerRank()) { + auto msg = std::make_shared(msgSize); + auto *header = + reinterpret_cast(msg->data); + header->type = TERMINATED; + header->senderRank = workerRank(); + header->numTerm = term; + mpi::messaging::sendTo(i, myId, msg); + } + } +} + +} // namespace mpi +} // namespace ospray diff --git a/modules/mpi/ospray/common/DynamicLoadBalancer.h b/modules/mpi/ospray/common/DynamicLoadBalancer.h new file mode 100644 index 0000000000..c84a47231a --- /dev/null +++ b/modules/mpi/ospray/common/DynamicLoadBalancer.h @@ -0,0 +1,138 @@ +// Copyright 2009-2022 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include +#include +#include +#include +#include "../common/Messaging.h" + +namespace ospray { +namespace mpi { + +enum MSG +{ + TERMINATED = 1000, + NEED_WORK = 2000, + VICTIM_NO_WORK = 3000, + RECV_WORK = 4000 + +}; + +struct Work +{ + int ntasks; + int offset; + int ownerRank; +}; + +struct DynamicLBMessage +{ + int type; + int senderRank; +}; + +struct DynamicLBTerminatedMessage : DynamicLBMessage +{ + int numTerm; +}; + +struct DynamicLBSendWorkMessage : DynamicLBMessage +{ + int numWorkItems; +}; + +struct DynamicLoadBalancer : public mpi::messaging::MessageHandler +{ + DynamicLoadBalancer(ObjectHandle myHandle, int numTasks); + virtual ~DynamicLoadBalancer() = default; + + void setActiveTasks(int numTasks); + void updateActiveTasks(int term); + int getActiveTasks(); + void addWork(Work workItem); + int getWorkSize(); + Work getWorkItemFront(); + Work getWorkItemBack(); + + void requestWork(); + void sendTerm(int term); + void incoming(const std::shared_ptr &message) override; + + private: + void sendWork(int thiefID); + void sendMultiWork(); + void sendWorkToThief( + int thiefID, const Work *toSend, const size_t numWorkItems); + + /* Request a variable size number of work items based on the current size of + * the workQueue. computeItemsRequested will be called after acquiring the + * workMutex to compute the number of items to be returned. + * computeItemsRequested should take a size_t and return a size_t representing + * the number of items to return. If computeItemsRequested returns 0 or more + * than workQueue.size(), an empty vector will be returned + */ + template + std::vector getWorkItems(const Fn computeItemsRequested); + + /* + The number of lifelines is determined using the following formula: + base ^ L-1 < #ranks <= base ^ L, where L = number of lifelines + for example 8 ranks can be represented as one of the following options: + 1- base 2, L = 3: 2 ^ 2 < 8 <= 2 ^ 3 + 2- base 3, L = 2, 3 ^ 1 < 8 <= 3 ^ 2 + 3- base 4, L = 2, 4 ^ 1 < 8 <= 4 ^ 2 + + After choosing the base, rankToCoords creates a virtual cyclic graph, where + ranks are the graph nodes, and the coords for each graph node(rank) is + represented in that base. The function rankToCoords returns the coords for + the passed rank depending on the base and power. For example if the base is + 2 and power then the coords are represented in zeros and ones, the power + identify the number of digits representing each rank coords, if the power is + 3, then the coords are represented as 3 digits. The function coordsToRank, + takes the rank coords and return the rank id. + */ + std::vector rankToCoords( + int rank, unsigned int base, unsigned int power); + unsigned int coordsToRank(std::vector &coords, + unsigned int base, + unsigned int power, + int numRanks); + std::vector getLifelineID(std::vector &coords, + unsigned base, + unsigned int power, + int numRanks); + std::vector getMyLifeLines( + int myRank, int numRanks, unsigned int base, unsigned int power); + + std::vector myLifelines; + std::set thiefIds; + std::deque workQueue; + int activeTasks; + std::mutex termMutex; + std::mutex workMutex; + std::mutex thiefMutex; +}; // struct DynamicLoadBalancer + +template +std::vector DynamicLoadBalancer::getWorkItems( + const Fn computeItemsRequested) +{ + std::vector reqWorkItems; + std::lock_guard guard(workMutex); + const size_t numReqWorkItems = computeItemsRequested(workQueue.size()); + if (numReqWorkItems == 0 || numReqWorkItems > workQueue.size()) { + return reqWorkItems; + } + for (int i = 0; i < numReqWorkItems; i++) { + Work workItem = workQueue.back(); + workQueue.pop_back(); + reqWorkItems.push_back(workItem); + } + return reqWorkItems; +} + +} // namespace mpi +} // namespace ospray diff --git a/modules/mpi/ospray/fb/DistributedFrameBuffer.cpp b/modules/mpi/ospray/fb/DistributedFrameBuffer.cpp index 2ae5adc1a6..4e881d01cb 100644 --- a/modules/mpi/ospray/fb/DistributedFrameBuffer.cpp +++ b/modules/mpi/ospray/fb/DistributedFrameBuffer.cpp @@ -101,6 +101,10 @@ void DFB::commit() findFirstFrameOperation(); } +mpicommon::Group DFB::getMPIGroup() +{ + return mpiGroup; +} void DFB::startNewFrame(const float errorThreshold) { { diff --git a/modules/mpi/ospray/fb/DistributedFrameBuffer.h b/modules/mpi/ospray/fb/DistributedFrameBuffer.h index 7174e3e3c5..8c5425492f 100644 --- a/modules/mpi/ospray/fb/DistributedFrameBuffer.h +++ b/modules/mpi/ospray/fb/DistributedFrameBuffer.h @@ -76,6 +76,7 @@ struct DistributedFrameBuffer : public mpi::messaging::MessageHandler, std::shared_ptr getTileOperation(); const Renderer *getLastRenderer() const; + mpicommon::Group getMPIGroup(); // ================================================================== // interface for maml messaging, enables communication between // different instances of same object diff --git a/modules/mpi/ospray/render/DistributedLoadBalancer.cpp b/modules/mpi/ospray/render/DistributedLoadBalancer.cpp index 0d040ce95d..ea5a460e4c 100644 --- a/modules/mpi/ospray/render/DistributedLoadBalancer.cpp +++ b/modules/mpi/ospray/render/DistributedLoadBalancer.cpp @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "DistributedLoadBalancer.h" @@ -12,6 +12,7 @@ #include "common/Profiling.h" #include "distributed/DistributedRenderer.h" #include "rkcommon/tasking/parallel_for.h" +#include "rkcommon/utility/getEnvVar.h" namespace ospray { namespace mpi { @@ -19,6 +20,12 @@ namespace staticLoadBalancer { using namespace mpicommon; using namespace rkcommon; +Distributed::Distributed() {} + +Distributed::~Distributed() +{ + handle.free(); +} void Distributed::renderFrame( FrameBuffer *_fb, Renderer *_renderer, Camera *camera, World *_world) { @@ -231,13 +238,21 @@ void Distributed::renderFrame( renderer->endFrame(dfb, perFrameData); dfb->endFrame(renderer->errorThreshold, camera); -} +} // end func +// *************************************************************************** void Distributed::renderFrameReplicated(DistributedFrameBuffer *fb, Renderer *renderer, Camera *camera, DistributedWorld *world) { + bool askedForWork = false; + int enableStaticBalancer; + auto OSPRAY_STATIC_BALANCER = + utility::getEnvVar("OSPRAY_STATIC_BALANCER"); + + enableStaticBalancer = OSPRAY_STATIC_BALANCER.value_or(0); + std::shared_ptr tileOperation = nullptr; if (fb->getLastRenderer() != renderer) { tileOperation = std::make_shared(); @@ -260,7 +275,6 @@ void Distributed::renderFrameReplicated(DistributedFrameBuffer *fb, const int ALLTASKS = fb->getTotalTiles(); int NTASKS = ALLTASKS / workerSize(); - // NOTE(jda) - If all tiles do not divide evenly among all worker ranks // (a.k.a. ALLTASKS / worker.size has a remainder), then // some ranks will have one extra tile to do. Thus NTASKS @@ -268,38 +282,103 @@ void Distributed::renderFrameReplicated(DistributedFrameBuffer *fb, if ((ALLTASKS % workerSize()) > workerRank()) NTASKS++; + // do not pass all tasks at once, this way if other ranks want to steal work, + // they can + int maxTasksPerRound = 20; + int numRounds = std::max(NTASKS / maxTasksPerRound, 1); + int tasksPerRound = NTASKS / numRounds; + int remainTasks = NTASKS % numRounds; + int terminatedTasks = 0; + int minActiveTasks = (ALLTASKS / workerSize()) * 0.25; + + std::unique_ptr dynamicLB = + make_unique(handle, ALLTASKS); + + mpicommon::barrier(fb->getMPIGroup().comm).wait(); + int totalActiveTasks = ALLTASKS; + // push current work to workQueue + for (int i = 0; i < numRounds; i++) { + Work myWork; + myWork.ntasks = tasksPerRound; + myWork.offset = i * tasksPerRound; + myWork.ownerRank = workerRank(); + dynamicLB->addWork(myWork); + } + + // Extra round for any remainder tasks + if (remainTasks > 0) { + Work myWork; + myWork.ntasks = remainTasks; + myWork.offset = numRounds * tasksPerRound; + myWork.ownerRank = workerRank(); + dynamicLB->addWork(myWork); + } #ifdef ENABLE_PROFILING start = ProfilingPoint(); #endif /* TODO WILL: This can dispatch back to LocalTiledLoadBalancer::renderTiles * to render the tiles instead of repeating this loop here ourselves. */ - tasking::parallel_for(NTASKS, [&](int taskIndex) { - const size_t tileID = taskIndex * workerSize() + workerRank(); - const size_t numTiles_x = fb->getNumTiles().x; - const size_t tile_y = tileID / numTiles_x; - const size_t tile_x = tileID - tile_y * numTiles_x; - const vec2i tileId(tile_x, tile_y); - const int32 accumID = fb->accumID(tileId); - - if (fb->tileError(tileId) <= renderer->errorThreshold) - return; + + while (0 < totalActiveTasks) { + int currentTasks = 0; + int offset = 0; + int ownerRank; + if (0 < dynamicLB->getWorkSize()) { + Work currWorkItem = dynamicLB->getWorkItemFront(); + currentTasks = currWorkItem.ntasks; + offset = currWorkItem.offset; + ownerRank = currWorkItem.ownerRank; + + askedForWork = false; + } + + if (0 < currentTasks) { + tasking::parallel_for(currentTasks, [&](int taskIndex) { + const size_t tileID = (taskIndex + offset) * workerSize() + ownerRank; + const size_t numTiles_x = fb->getNumTiles().x; + const size_t tile_y = tileID / numTiles_x; + const size_t tile_x = tileID - tile_y * numTiles_x; + const vec2i tileId(tile_x, tile_y); + const int32 accumID = fb->accumID(tileId); + + if (fb->tileError(tileId) <= renderer->errorThreshold) + return; #if TILE_SIZE > MAX_TILE_SIZE - auto tilePtr = make_unique(tileId, fbSize, accumID); - auto &tile = *tilePtr; + auto tilePtr = make_unique(tileId, fbSize, accumID); + auto &tile = *tilePtr; #else Tile __aligned(64) tile(tileId, fbSize, accumID); #endif - if (!fb->frameCancelled()) { - tasking::parallel_for(numJobs(renderer->spp, accumID), [&](size_t tid) { - renderer->renderTile(fb, camera, world, perFrameData, tile, tid); + if (!fb->frameCancelled()) { + tasking::parallel_for( + numJobs(renderer->spp, accumID), [&](size_t tid) { + renderer->renderTile( + fb, camera, world, perFrameData, tile, tid); + }); + } + + fb->setTile(tile); }); } - - fb->setTile(tile); - }); + dynamicLB->sendTerm(currentTasks); + terminatedTasks = terminatedTasks + currentTasks; + + if (0 < terminatedTasks) + dynamicLB->updateActiveTasks(terminatedTasks); + totalActiveTasks = dynamicLB->getActiveTasks(); + terminatedTasks = 0; + + // if the total active tasks over all workers is more than some min, request + // work + if (currentTasks <= 0 && minActiveTasks < totalActiveTasks && !askedForWork + && !enableStaticBalancer) { + dynamicLB->requestWork(); + askedForWork = true; + } + } #ifdef ENABLE_PROFILING end = ProfilingPoint(); std::cout << "Render loop took: " << elapsedTimeMs(start, end) @@ -328,11 +407,16 @@ void Distributed::renderFrameReplicated(DistributedFrameBuffer *fb, #endif } +// *************************************************************************** std::string Distributed::toString() const { return "ospray::mpi::staticLoadBalancer::Distributed"; } +void Distributed::setObjectHandle(ObjectHandle &handle_) +{ + handle = handle_; +} void Distributed::renderTiles(FrameBuffer *, Renderer *, Camera *, diff --git a/modules/mpi/ospray/render/DistributedLoadBalancer.h b/modules/mpi/ospray/render/DistributedLoadBalancer.h index 8e254ce322..bd9ff107b6 100644 --- a/modules/mpi/ospray/render/DistributedLoadBalancer.h +++ b/modules/mpi/ospray/render/DistributedLoadBalancer.h @@ -1,9 +1,10 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once #include "../common/DistributedWorld.h" +#include "../common/DynamicLoadBalancer.h" #include "../fb/DistributedFrameBuffer.h" #include "camera/Camera.h" #include "common/World.h" @@ -21,6 +22,8 @@ namespace staticLoadBalancer { */ struct Distributed : public TiledLoadBalancer { + Distributed(); + ~Distributed() override; void renderFrame(FrameBuffer *fb, Renderer *renderer, Camera *camera, @@ -31,6 +34,8 @@ struct Distributed : public TiledLoadBalancer Camera *camera, DistributedWorld *world); + void setObjectHandle(ObjectHandle &handle_); + /* Not implemented by Distributed load balancer currently, * this could potentially be useful to implement later to manage * the actual tile list rendering after computing the list of tiles @@ -44,6 +49,9 @@ struct Distributed : public TiledLoadBalancer void *perFrameData) override; std::string toString() const override; + + private: + ObjectHandle handle; }; } // namespace staticLoadBalancer diff --git a/modules/mpi/ospray/render/ThreadedRenderTask.h b/modules/mpi/ospray/render/ThreadedRenderTask.h index 3fe4dcc4c2..61d4ebd2ae 100644 --- a/modules/mpi/ospray/render/ThreadedRenderTask.h +++ b/modules/mpi/ospray/render/ThreadedRenderTask.h @@ -1,4 +1,4 @@ -// Copyright 2009-2020 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once @@ -16,7 +16,9 @@ namespace mpi { */ struct ThreadedRenderTask : public Future { - ThreadedRenderTask(FrameBuffer *, std::function fcn); + ThreadedRenderTask(FrameBuffer *, + const std::shared_ptr &, + std::function fcn); ~ThreadedRenderTask() override; bool isFinished(OSPSyncEvent event = OSP_TASK_FINISHED) override; @@ -29,6 +31,7 @@ struct ThreadedRenderTask : public Future private: Ref fb; + std::shared_ptr loadBalancer; std::atomic taskDuration{0.f}; std::atomic finished; std::thread thread; @@ -36,9 +39,10 @@ struct ThreadedRenderTask : public Future // Inlined definitions ////////////////////////////////////////////////////// -inline ThreadedRenderTask::ThreadedRenderTask( - FrameBuffer *_fb, std::function fcn) - : fb(_fb), finished(false) +inline ThreadedRenderTask::ThreadedRenderTask(FrameBuffer *_fb, + const std::shared_ptr &_loadBalancer, + std::function fcn) + : fb(_fb), loadBalancer(_loadBalancer), finished(false) { thread = std::thread([this, fcn]() { taskDuration = fcn(); @@ -80,7 +84,7 @@ inline float ThreadedRenderTask::getProgress() inline float ThreadedRenderTask::getTaskDuration() { - return taskDuration.load(); + return taskDuration.load(); } } // namespace mpi From 765991f40bd0b53b9defb6ea3e74863bedc97e05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20G=C3=BCnther?= Date: Mon, 17 Jan 2022 15:26:10 +0100 Subject: [PATCH 20/26] Refactor SpotLight - replace world2light and light2world by c0 and c90 - calc invarient AngularAttenuation and IntensityDistr before branching --- modules/cpu/lights/SpotLight.ispc | 107 ++++++++++++++---------------- 1 file changed, 48 insertions(+), 59 deletions(-) diff --git a/modules/cpu/lights/SpotLight.ispc b/modules/cpu/lights/SpotLight.ispc index 2c955fff05..d5f3439702 100644 --- a/modules/cpu/lights/SpotLight.ispc +++ b/modules/cpu/lights/SpotLight.ispc @@ -11,10 +11,6 @@ struct SpotLight vec3f position; // Position of the SpotLight vec3f direction; - vec3f c0; - linear3f light2world; // coordinate frame, with vz == direction that the - // SpotLight is emitting - linear3f world2light; vec3f intensity; // RGB radiative intensity of the SpotLight vec3f radiance; // emitted RGB radiance float cosAngleMax; // Angular limit of the spot in an easier to use form: @@ -26,9 +22,12 @@ struct SpotLight float innerRadius; // defines the size of the inner cut out of the DiskLight // to represent a RingLight float areaPdf; // pdf of disk/ring with radius/innerRadius + float *lid; // luminance intensity distribution vec2i size; vec2f scale; + vec3f c0; // orientation of lid + vec3f c90; }; // Implementation @@ -38,8 +37,11 @@ inline float AngularAttenuation(const SpotLight *uniform self, float cosAngle) return clamp((cosAngle - self->cosAngleMax) * self->cosAngleScale); } -inline float IntensityDistr( - const SpotLight *uniform self, float cosAngle, const vec3f &localDir) +inline float IntensityDistr(const SpotLight *uniform self, + float cosAngle, + const uniform vec3f &c0, + const uniform vec3f &c90, + const vec3f &dir) { const float u = acos(cosAngle) * self->scale.x; const float u_frac = frac(u); @@ -50,7 +52,7 @@ inline float IntensityDistr( if (self->size.y == 1) { // 1D intensity = lerp(u_frac, self->lid[u0], self->lid[u1]); } else { // 2D - const float v = (atan2(localDir.y, localDir.x) + pi) * self->scale.y; + const float v = (atan2(dot(dir, c90), dot(dir, c0)) + pi) * self->scale.y; const float v_frac = frac(v); const int v0 = v; const int v1 = (v0 + 1 >= self->size.y) ? 0 : v0 + 1; @@ -65,8 +67,9 @@ inline float IntensityDistr( inline Light_SampleRes Sample(const SpotLight *uniform self, const uniform vec3f &pos, - const uniform linear3f &l2w, - const uniform linear3f &w2l, + const uniform vec3f &dir, + const uniform vec3f &c0, + const uniform vec3f &c90, const DifferentialGeometry &dg, const vec2f &s) { @@ -75,9 +78,14 @@ inline Light_SampleRes Sample(const SpotLight *uniform self, // extant light vector from the hit point res.dir = pos - dg.P; - if (self->radius > 0.) + if (self->radius > 0.0f) { + uniform linear3f l2w; + l2w.vx = c0; + l2w.vy = c90; + l2w.vz = dir; res.dir = l2w * uniformSampleRing(self->radius, self->innerRadius, s) + res.dir; + } const float dist2 = dot(res.dir, res.dir); const float invdist = rsqrt(dist2); @@ -87,36 +95,34 @@ inline Light_SampleRes Sample(const SpotLight *uniform self, res.dist = dist2 * invdist; // cosine of the negated light direction and light vector. - const float cosAngle = -dot(l2w.vz, res.dir); + const float cosAngle = -dot(dir, res.dir); - if (self->radius > 0.) { + float weight = AngularAttenuation(self, cosAngle); + if (self->lid) + weight *= IntensityDistr(self, cosAngle, c0, c90, res.dir); + if (self->radius > 0.0f) { // convert area PDF to solid angle PDF res.pdf = self->areaPdf * (dist2 / abs(cosAngle)); - // calculate the attenuated emitted radiance - float attenuation = AngularAttenuation(self, cosAngle); - if (!self->lid) { - res.weight = (self->radiance * attenuation) / res.pdf; - } else { + if (self->lid) { // when an light distribution function is used we want to // remove the cosine term. To avoid numerical issues // at cosineAngle = 0 we use the fact that the division // of radiance with the cosine cancels out. - attenuation *= IntensityDistr(self, cosAngle, w2l * res.dir); - res.weight = (self->radiance * attenuation) / (self->areaPdf * dist2); + weight /= self->areaPdf * dist2; + } else { + weight /= res.pdf; } + res.weight = self->radiance * weight; } else { res.pdf = inf; // we always take this sample - float attenuation = AngularAttenuation(self, cosAngle); - if (self->lid) { - attenuation *= IntensityDistr(self, cosAngle, w2l * res.dir); - } else { + if (!self->lid) { // if the spotlight does not use a measured LDF we // simulate Lambertian behavior by multiplication with cosineAngle - attenuation *= abs(cosAngle); + weight *= abs(cosAngle); } // convert from intensity to radiance by attenuating by distance^2; // attenuate by angle - res.weight = self->intensity * (sqr(invdist) * attenuation); + res.weight = self->intensity * (sqr(invdist) * weight); } return res; } @@ -129,7 +135,7 @@ Light_SampleRes SpotLight_sample(const Light *uniform super, const SpotLight *uniform self = (SpotLight * uniform) super; assert(self); return Sample( - self, self->position, self->light2world, self->world2light, dg, s); + self, self->position, self->direction, self->c0, self->c90, dg, s); } Light_SampleRes SpotLight_sample_instanced(const Light *uniform super, @@ -150,20 +156,11 @@ Light_SampleRes SpotLight_sample_instanced(const Light *uniform super, // transform light into the world space const uniform vec3f pos = xfmPoint(xfm, self->position); const uniform vec3f dir = normalize(xfmVector(xfm, self->direction)); - const uniform vec3f c0 = normalize(xfmVector(xfm, self->c0)); - - // prepare spotlight to world transformation matrix - uniform linear3f l2w; - l2w.vz = dir; - l2w.vy = normalize(cross(c0, l2w.vz)); - l2w.vx = cross(l2w.vz, l2w.vy); - - // prepare world to spotlight transformation matrix, - // this is orthonormal transformation so transposing is enough - const uniform linear3f w2l = transposed(l2w); + const uniform vec3f c90 = normalize(cross(xfmVector(xfm, self->c0), dir)); + const uniform vec3f c0 = cross(dir, c90); // sample light - res = Sample(self, pos, l2w, w2l, dg, s); + res = Sample(self, pos, dir, c0, c90, dg, s); } return res; } @@ -171,7 +168,8 @@ Light_SampleRes SpotLight_sample_instanced(const Light *uniform super, inline Light_EvalRes Eval(const SpotLight *uniform self, const uniform vec3f &position, const uniform vec3f &direction, - const uniform linear3f &w2l, + const uniform vec3f &c0, + const uniform vec3f &c90, const DifferentialGeometry &dg, const vec3f &dir, const float minDist, @@ -193,7 +191,7 @@ inline Light_EvalRes Eval(const SpotLight *uniform self, if (and(d2radius), d2> sqr(self->innerRadius))) { float attenuation = AngularAttenuation(self, cosAngle); if (self->lid) { - attenuation *= IntensityDistr(self, cosAngle, w2l * dir); + attenuation *= IntensityDistr(self, cosAngle, c0, c90, dir); // since the light distribution function already includes // the cosine term we remove it from the Lambertian area @@ -225,7 +223,8 @@ Light_EvalRes SpotLight_eval(const Light *uniform super, return Eval(self, self->position, self->direction, - self->world2light, + self->c0, + self->c90, dg, dir, minDist, @@ -252,19 +251,11 @@ Light_EvalRes SpotLight_eval_instanced(const Light *uniform super, // transform light into the world space const uniform vec3f position = xfmPoint(xfm, self->position); const uniform vec3f direction = normalize(xfmVector(xfm, self->direction)); + const uniform vec3f c90 = + normalize(cross(xfmVector(xfm, self->c0), direction)); + const uniform vec3f c0 = cross(direction, c90); - // prepare world to spotlight transformation matrix, - // this is orthonormal transformation so transposing is enough - const uniform vec3f c0 = normalize(xfmVector(xfm, self->c0)); - const uniform vec3f vy = normalize(cross(c0, direction)); - const uniform vec3f vx = cross(direction, vy); - uniform linear3f w2l; - w2l.vx = make_vec3f(vx.x, vy.x, direction.x); - w2l.vy = make_vec3f(vx.y, vy.y, direction.y); - w2l.vz = make_vec3f(vx.z, vy.z, direction.z); - - // evaluate light - res = Eval(self, position, direction, w2l, dg, dir, minDist, maxDist); + res = Eval(self, position, direction, c0, c90, dg, dir, minDist, maxDist); } return res; } @@ -289,7 +280,7 @@ export void SpotLight_set(void *uniform super, self->position = position; self->direction = normalize(direction); - self->c0 = normalize(c0); + self->c0 = c0; // Enable dynamic runtime instancing or apply static transformation const Instance *uniform instance = self->super.instance; @@ -300,13 +291,12 @@ export void SpotLight_set(void *uniform super, } else { self->position = xfmPoint(instance->xfm, self->position); self->direction = normalize(xfmVector(instance->xfm, self->direction)); - self->c0 = normalize(xfmVector(instance->xfm, self->c0)); + self->c0 = xfmVector(instance->xfm, c0); } } - self->light2world.vz = self->direction; - self->light2world.vy = normalize(cross(self->c0, self->light2world.vz)); - self->light2world.vx = cross(self->light2world.vz, self->light2world.vy); + self->c90 = normalize(cross(self->c0, self->direction)); + self->c0 = cross(self->direction, self->c90); self->radiance = radiance; self->intensity = intensity; @@ -317,7 +307,6 @@ export void SpotLight_set(void *uniform super, self->areaPdf = uniformSampleRingPDF(radius, innerRadius); self->lid = lid; if (self->lid) { - self->world2light = rcp(self->light2world); self->size = size; self->scale = make_vec2f(nextafter((size.x - 1) * one_over_pi, -1.0f), nextafter(self->size.y * one_over_two_pi, -1.0f)); From 95f75895413e89b87c54e7c5067220f299b36983 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20G=C3=BCnther?= Date: Fri, 14 Jan 2022 16:37:07 +0100 Subject: [PATCH 21/26] Lumious intensity distribution also for quad and sphere light --- CHANGELOG.md | 4 + apps/ospTestSuite/test_light.cpp | 70 +++-- apps/ospTestSuite/test_light.h | 10 +- doc/Makefile | 2 +- doc/api.md | 113 ++++---- doc/{spot_coords.fig => c-gamma_coords.fig} | 0 doc/images.md | 2 +- doc/quad_light.fig | 13 +- modules/cpu/CMakeLists.txt | 3 +- modules/cpu/lights/AmbientLight.cpp | 6 +- modules/cpu/lights/DirectionalLight.cpp | 6 +- modules/cpu/lights/HDRILight.cpp | 6 +- modules/cpu/lights/IntensityDistribution.cpp | 33 +++ modules/cpu/lights/IntensityDistribution.h | 30 +++ modules/cpu/lights/IntensityDistribution.ih | 52 ++++ modules/cpu/lights/PointLight.cpp | 64 +++-- modules/cpu/lights/PointLight.h | 5 +- modules/cpu/lights/PointLight.ispc | 112 ++++++-- modules/cpu/lights/QuadLight.cpp | 45 +++- modules/cpu/lights/QuadLight.h | 4 +- modules/cpu/lights/QuadLight.ispc | 253 ++++++++++-------- modules/cpu/lights/SpotLight.cpp | 97 +++---- modules/cpu/lights/SpotLight.h | 8 +- modules/cpu/lights/SpotLight.ispc | 167 ++++-------- modules/cpu/lights/SunSkyLight.cpp | 6 +- ...hotometricLight_Light_parameter_10.png.md5 | 1 + ...hotometricLight_Light_parameter_11.png.md5 | 1 + ...PhotometricLight_Light_parameter_4.png.md5 | 1 + ...PhotometricLight_Light_parameter_5.png.md5 | 1 + ...PhotometricLight_Light_parameter_6.png.md5 | 1 + ...PhotometricLight_Light_parameter_7.png.md5 | 1 + ...PhotometricLight_Light_parameter_8.png.md5 | 1 + ...PhotometricLight_Light_parameter_9.png.md5 | 1 + ...hotometricLight_Light_parameter_10.png.md5 | 1 + ...hotometricLight_Light_parameter_11.png.md5 | 1 + ...PhotometricLight_Light_parameter_4.png.md5 | 1 + ...PhotometricLight_Light_parameter_5.png.md5 | 1 + ...PhotometricLight_Light_parameter_6.png.md5 | 1 + ...PhotometricLight_Light_parameter_7.png.md5 | 1 + ...PhotometricLight_Light_parameter_8.png.md5 | 1 + ...PhotometricLight_Light_parameter_9.png.md5 | 1 + 41 files changed, 690 insertions(+), 437 deletions(-) rename doc/{spot_coords.fig => c-gamma_coords.fig} (100%) create mode 100644 modules/cpu/lights/IntensityDistribution.cpp create mode 100644 modules/cpu/lights/IntensityDistribution.h create mode 100644 modules/cpu/lights/IntensityDistribution.ih create mode 100644 test_image_data/baseline/AVX2/PhotometricLight_Light_parameter_10.png.md5 create mode 100644 test_image_data/baseline/AVX2/PhotometricLight_Light_parameter_11.png.md5 create mode 100644 test_image_data/baseline/AVX2/PhotometricLight_Light_parameter_4.png.md5 create mode 100644 test_image_data/baseline/AVX2/PhotometricLight_Light_parameter_5.png.md5 create mode 100644 test_image_data/baseline/AVX2/PhotometricLight_Light_parameter_6.png.md5 create mode 100644 test_image_data/baseline/AVX2/PhotometricLight_Light_parameter_7.png.md5 create mode 100644 test_image_data/baseline/AVX2/PhotometricLight_Light_parameter_8.png.md5 create mode 100644 test_image_data/baseline/AVX2/PhotometricLight_Light_parameter_9.png.md5 create mode 100644 test_image_data/baseline/AVX512SKX/PhotometricLight_Light_parameter_10.png.md5 create mode 100644 test_image_data/baseline/AVX512SKX/PhotometricLight_Light_parameter_11.png.md5 create mode 100644 test_image_data/baseline/AVX512SKX/PhotometricLight_Light_parameter_4.png.md5 create mode 100644 test_image_data/baseline/AVX512SKX/PhotometricLight_Light_parameter_5.png.md5 create mode 100644 test_image_data/baseline/AVX512SKX/PhotometricLight_Light_parameter_6.png.md5 create mode 100644 test_image_data/baseline/AVX512SKX/PhotometricLight_Light_parameter_7.png.md5 create mode 100644 test_image_data/baseline/AVX512SKX/PhotometricLight_Light_parameter_8.png.md5 create mode 100644 test_image_data/baseline/AVX512SKX/PhotometricLight_Light_parameter_9.png.md5 diff --git a/CHANGELOG.md b/CHANGELOG.md index f2a9abf439..bdd8262c99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,10 @@ Version History offload device. - MPI: Resolve possible race condition in assigning IDs to distributed objects +- Support for photometric lights (e.g., IES or EULUMDAT) also for + `sphere` and `quad` lights. When setting `intensityDistribution`, + other values for `intensityQuantity` than + `OSP_INTENSITY_QUANTITY_SCALE` are deprecated ### Changes in v2.8.0: diff --git a/apps/ospTestSuite/test_light.cpp b/apps/ospTestSuite/test_light.cpp index adba49fa29..29421ba4a4 100644 --- a/apps/ospTestSuite/test_light.cpp +++ b/apps/ospTestSuite/test_light.cpp @@ -1,4 +1,4 @@ -// Copyright 2020-2021 Intel Corporation +// Copyright 2020-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "test_light.h" @@ -171,37 +171,74 @@ void GeometricLight::SetUp() PhotometricLight::PhotometricLight() { auto params = GetParam(); - radius = std::get<0>(params); - rendererType = std::get<1>(params); + lightType = std::get<0>(params); + size = std::get<1>(params); + rendererType = std::get<2>(params); + + // area lights need a minimum size + if (lightType == "quad") + size = std::max(0.01f, size); } void PhotometricLight::SetUp() { LightTest::SetUp(); - cpp::Light light1d("spot"); + const vec3f pos1 = xfmPoint(xfm, vec3f(-0.6f, 0.8f, -0.5f)); + const vec3f pos2 = xfmPoint(xfm, vec3f(0.3f, 0.6f, 0.f)); + const vec3f dir = xfmVector(xfm, vec3f(0.0f, -1.0f, 0.0f)); + const vec3f edge1 = xfmVector(xfm, vec3f(1.0f, 0.0f, 0.0f)); + const vec3f edge2 = xfmVector(xfm, vec3f(0.0f, 0.0f, 1.0f)); + + cpp::Light light1d(lightType); light1d.setParam("intensity", 5.f); - light1d.setParam("position", xfmPoint(xfm, vec3f(-0.6f, 0.8f, -0.5f))); - light1d.setParam("direction", xfmVector(xfm, vec3f(0.0f, -1.0f, 0.0f))); - light1d.setParam("openingAngle", 360.f); - light1d.setParam("penumbraAngle", 0.f); - light1d.setParam("radius", radius); + light1d.setParam("intensityQuantity", OSP_INTENSITY_QUANTITY_SCALE); float lid1d[] = {2.5f, 0.4f, 0.2f, 0.1f, 0.03f, 0.01f, 0.01f}; light1d.setParam("intensityDistribution", cpp::CopiedData(lid1d, 7)); + + if (lightType == "spot") { + light1d.setParam("position", pos1); + light1d.setParam("direction", dir); + light1d.setParam("openingAngle", 360.f); + light1d.setParam("penumbraAngle", 0.f); + light1d.setParam("radius", size); + } else if (lightType == "quad") { + light1d.setParam("position", pos1 - size * edge1 - size * edge2); + light1d.setParam("edge1", 2.0f * size * edge1); + light1d.setParam("edge2", 2.0f * size * edge2); + } else if (lightType == "sphere") { + light1d.setParam("position", pos1); + light1d.setParam("radius", size); + light1d.setParam("direction", dir); + } + light1d.commit(); - cpp::Light light2d("spot"); + cpp::Light light2d(lightType); light2d.setParam("intensity", 1.f); - light2d.setParam("position", xfmPoint(xfm, vec3f(0.3f, 0.6f, 0.f))); - light2d.setParam("direction", xfmVector(xfm, vec3f(0.0f, -1.0f, 0.0f))); - light2d.setParam("openingAngle", 270.f); - light2d.setParam("penumbraAngle", 10.f); - light2d.setParam("radius", radius); + light2d.setParam("intensityQuantity", OSP_INTENSITY_QUANTITY_SCALE); float lid2d[60] = { 1.5f, 5.0f, 6.0f, 0.3f, 0.01f, 0.15f, 0.5f, 1.6f, 0.1f, 0.01f}; light2d.setParam( "intensityDistribution", cpp::CopiedData(lid2d, vec2ul(5, 12))); light2d.setParam("c0", xfmVector(xfm, vec3f(1.0f, 0.0f, 0.0f))); + + if (lightType == "spot") { + light2d.setParam("position", pos2); + light2d.setParam("direction", dir); + light2d.setParam("openingAngle", 270.f); + light2d.setParam("penumbraAngle", 10.f); + light2d.setParam("radius", size); + } else if (lightType == "quad") { + light2d.setParam("position", pos2 - size * edge1 - size * edge2); + light2d.setParam("edge1", 2.0f * size * edge1); + light2d.setParam("edge2", 2.0f * size * edge2); + } else if (lightType == "sphere") { + light2d.setParam("position", pos2); + light2d.setParam("radius", size); + light2d.setParam("direction", dir); + } + light2d.commit(); cpp::Group group; @@ -587,7 +624,8 @@ TEST_P(PhotometricLight, parameter) INSTANTIATE_TEST_SUITE_P(Light, PhotometricLight, - ::testing::Combine(::testing::Values(0.0f, 0.1f), + ::testing::Combine(::testing::Values("spot", "quad", "sphere"), + ::testing::Values(0.0f, 0.1f), ::testing::Values("scivis", "pathtracer"))); // HDRI Light diff --git a/apps/ospTestSuite/test_light.h b/apps/ospTestSuite/test_light.h index 333d9baa51..5bd673e9f0 100644 --- a/apps/ospTestSuite/test_light.h +++ b/apps/ospTestSuite/test_light.h @@ -1,4 +1,4 @@ -// Copyright 2020-2021 Intel Corporation +// Copyright 2020-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "test_fixture.h" @@ -71,15 +71,17 @@ class GeometricLight class PhotometricLight : public LightTest, - public ::testing::TestWithParam< - std::tuple> + public ::testing::TestWithParam> { public: PhotometricLight(); void SetUp() override; private: - float radius{0.0}; + std::string lightType; + float size{0.0}; }; class QuadLight : public LightTest, diff --git a/doc/Makefile b/doc/Makefile index 17f7bc4c2b..a0fb488ccb 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -5,7 +5,7 @@ process_version := $(addprefix tmp/, $(addsuffix .md, tutorials getting_ospray r tmptexfiles := $(addprefix tmp/, $(addsuffix .tex, overview changelog compilation api tutorials)) images_jpg := $(addprefix images/, $(addsuffix .jpg, exampleViewer $(addprefix camera_, perspective architectural stereo orthographic panoramic) $(addprefix material_, OBJ Principled CarPaint Metal Alloy Glass ThinGlass MetallicPaint Luminous) ColoredWindow $(addprefix ospMPIDistribTutorial, Volume Spheres _firstFrame _accumulatedFrame))) images_png := $(addprefix images/, $(addsuffix .png, diffuse_rooms normalmap_frustum tutorial_accumulatedframe tutorial_firstframe ospExamples renderSunSky)) -images_fig := spot_light spot_coords quad_light hdri_light +images_fig := spot_light c-gamma_coords quad_light hdri_light images_svg := gitter_badge structured_spherical_coords vdb_structure images_fig2pdf := $(addprefix tmp/, $(addsuffix .pdf, $(images_fig))) diff --git a/doc/api.md b/doc/api.md index 0ed2d34993..e9d9d75fcf 100644 --- a/doc/api.md +++ b/doc/api.md @@ -1440,10 +1440,49 @@ specific light source). surface, unit is W/m^2^ OSP_INTENSITY_QUANTITY_SCALE a linear scaling factor for light sources with a - built-in quantity (e.g., `HDRI`, or `sunSky`). + built-in quantity (e.g., `HDRI`, or `sunSky`, or + when using `intensityDistribution`). ---------------------------------- ---------------------------------------------------- : Types of radiometric quantities used to interpret a light's `intensity` parameter. +### Photometric Lights + +Measured light sources (IES, EULUMDAT, ...) are supported by the +`sphere`, `spot`, and `quad` lights when setting an +`intensityDistribution` [data] array to modulate the intensity per +direction. The mapping is using the C-Ī³ coordinate system (see also +below figure): the values of the first (or only) dimension of +`intensityDistribution` are uniformly mapped to Ī³ in [0ā€“Ļ€]; the first +intensity value to 0, the last value to Ļ€, thus at least two values need +to be present. + +![C-Ī³ coordinate system for the mapping of `intensityDistribution` with +photometric lights.][imgCGammaCoords] + +If the array has a second dimension then the intensities are not +rotational symmetric around the main direction (where angle Ī³ is zero), +but are accordingly mapped to the C-halfplanes in [0ā€“2Ļ€]; the first +"row" of values to 0 and 2Ļ€, the other rows such that they have uniform +distance to its neighbors. The orientation of the C0-plane is specified +via `c0`. + + ---------- --------------------- --------------------------------------------- + Type Name Description + ---------- --------------------- --------------------------------------------- + float[] intensityDistribution luminous intensity distribution for + photometric lights; can be 2D for asymmetric + illumination; values are assumed to be + uniformly distributed + + vec3f c0 orientation, i.e., direction of the + C0-(half)plane (only needed if illumination + via `intensityDistribution` is asymmetric) + ---------- --------------------- --------------------------------------------- + : Special parameters for photometric lights. + +When using an `intensityDistribution` then the default and only valid +value for `intensityQuantity` is `OSP_INTENSITY_QUANTITY_SCALE`. + The following light types are supported by most OSPRay renderers. ### Directional Light / Distant Light @@ -1475,16 +1514,20 @@ The sphere light (or the special case point light) is a light emitting uniformly in all directions from the surface toward the outside. It does not emit any light toward the inside of the sphere. It is created by passing the type string "`sphere`" to `ospNewLight`. The point light -supports `OSP_INTENSITY_QUANTITY_POWER`, -`OSP_INTENSITY_QUANTITY_INTENSITY` (default) and -`OSP_INTENSITY_QUANTITY_RADIANCE` as `intensityQuantity` parameter value. -In addition to the [general parameters](#lights) understood by all -lights the sphere light supports the following special parameters: +supports only `OSP_INTENSITY_QUANTITY_SCALE` when +`intensityDistribution` is set, or otherwise +`OSP_INTENSITY_QUANTITY_POWER`, `OSP_INTENSITY_QUANTITY_INTENSITY` (then +default) and `OSP_INTENSITY_QUANTITY_RADIANCE` as `intensityQuantity` +parameter value. In addition to the [general parameters](#lights) +understood by all lights and the [photometric +parameters](#photometric-lights) the sphere light supports the following +special parameters: Type Name Default Description ------- ---------- ------------ -------------------------------- vec3f position $(0, 0, 0)$ the center of the sphere light float radius 0 the size of the sphere light + vec3f direction $(0, 0, 1)$ main orientation of `intensityDistribution` ------- ---------- ------------ -------------------------------- : Special parameters accepted by the sphere light. @@ -1492,16 +1535,19 @@ Setting the radius to a value greater than zero will result in soft shadows when the renderer uses stochastic sampling (like the [path tracer]). -### Spotlight / Photometric Light +### Spotlight / Ring Light The spotlight is a light emitting into a cone of directions. It is created by passing the type string "`spot`" to `ospNewLight`. The -spotlight supports `OSP_INTENSITY_QUANTITY_POWER`, -`OSP_INTENSITY_QUANTITY_INTENSITY` (default) and +spotlight +supports only `OSP_INTENSITY_QUANTITY_SCALE` when +`intensityDistribution` is set, or otherwise +`OSP_INTENSITY_QUANTITY_POWER`, +`OSP_INTENSITY_QUANTITY_INTENSITY` (then default) and `OSP_INTENSITY_QUANTITY_RADIANCE` as `intensityQuantity` parameter value. In addition to the [general parameters](#lights) understood by -all lights the spotlight supports the special parameters listed in the -table. +all lights and the [photometric parameters](#photometric-lights) the +spotlight supports the special parameters listed in the table. ---------- --------------------- ----------- --------------------------------- Type Name Default Description @@ -1529,18 +1575,6 @@ table. float innerRadius 0 in combination with `radius` turns the disk into a ring - - float[] intensityDistribution luminous intensity distribution - for photometric lights; can be 2D - for asymmetric illumination; - values are assumed to be - uniformly distributed - - vec3f c0 orientation, i.e., direction of - the C0-(half)plane (only needed - if illumination via - `intensityDistribution` is - asymmetric) ---------- --------------------- ----------- --------------------------------- : Special parameters accepted by the spotlight. @@ -1551,34 +1585,18 @@ shadows when the renderer uses stochastic sampling (like the [path tracer]). Additionally setting the inner radius will result in a ring instead of a disk emitting the light. -Measured light sources (IES, EULUMDAT, ...) are supported by providing -an `intensityDistribution` [data] array to modulate the intensity per -direction. The mapping is using the C-Ī³ coordinate system (see also -below figure): the values of the first (or only) dimension of -`intensityDistribution` are uniformly mapped to Ī³ in [0ā€“Ļ€]; the first -intensity value to 0, the last value to Ļ€, thus at least two values need -to be present. If the array has a second dimension then the intensities -are not rotational symmetric around `direction`, but are accordingly -mapped to the C-halfplanes in [0ā€“2Ļ€]; the first "row" of values to 0 and -2Ļ€, the other rows such that they have uniform distance to its -neighbors. The orientation of the C0-plane is specified via `c0`. -A combination of using an `intensityDistribution` and -`OSP_INTENSITY_QUANTITY_POWER` as `intensityQuantity` is not supported -at the moment. - -![C-Ī³ coordinate system for the mapping of `intensityDistribution` to -the spotlight.][imgSpotCoords] - ### Quad Light The quad^[actually a parallelogram] light is a planar, procedural area light source emitting uniformly on one side into the half-space. It is created by passing the type string "`quad`" to `ospNewLight`. The quad -light supports `OSP_INTENSITY_QUANTITY_POWER`, -`OSP_INTENSITY_QUANTITY_INTENSITY` and `OSP_INTENSITY_QUANTITY_RADIANCE` -(default) as `intensityQuantity` parameter. In addition to the [general -parameters](#lights) understood by all lights the quad light supports -the following special parameters: +light supports only `OSP_INTENSITY_QUANTITY_SCALE` when +`intensityDistribution` is set, or otherwise +`OSP_INTENSITY_QUANTITY_POWER`, `OSP_INTENSITY_QUANTITY_INTENSITY` and +`OSP_INTENSITY_QUANTITY_RADIANCE` (then default) as `intensityQuantity` +parameter. In addition to the [general parameters](#lights) understood +by all lights and the [photometric parameters](#photometric-lights) the +quad light supports the following special parameters: Type Name Default Description ------ --------- ------------ ----------------------------------------- @@ -1591,6 +1609,7 @@ the following special parameters: ![Defining a quad light which emits toward the reader.][imgQuadLight] The emission side is determined by the cross product of `edge1`Ɨ`edge2`. +which is also the main emission direction for `intensityDistribution`. Note that only renderers that use stochastic sampling (like the path tracer) will compute soft shadows from the quad light. Other renderers will just sample the center of the quad light, which results in hard @@ -1598,7 +1617,7 @@ shadows. ### Cylinder Light -The cylinder light is a cylinderical, procedural area light source +The cylinder light is a cylinderical, procedural area light source emitting uniformly outwardly into the space beyond the boundary. It is created by passing the type string "`cylinder`" to `ospNewLight`. The cylinder light supports `OSP_INTENSITY_QUANTITY_POWER`, diff --git a/doc/spot_coords.fig b/doc/c-gamma_coords.fig similarity index 100% rename from doc/spot_coords.fig rename to doc/c-gamma_coords.fig diff --git a/doc/images.md b/doc/images.md index fc4ed6f98b..2aa60a6d52 100644 --- a/doc/images.md +++ b/doc/images.md @@ -6,8 +6,8 @@ [imgMPIDistribTutorial2]: ospMPIDistribTutorial_accumulatedFrame.jpg { width=60% } [imgMPIDistribTutorialSpheres]: ospMPIDistribTutorialSpheres.jpg { width=60% } [imgMPIDistribTutorialVolume]: ospMPIDistribTutorialVolume.jpg { width=60% } +[imgCGammaCoords]: c-gamma_coords.fig [imgSpotLight]: spot_light.fig -[imgSpotCoords]: spot_coords.fig [imgQuadLight]: quad_light.fig [imgHDRILight]: hdri_light.fig [imgCameraPerspective]: camera_perspective.jpg { width=60% } diff --git a/doc/quad_light.fig b/doc/quad_light.fig index 935b31931a..075bb51a95 100644 --- a/doc/quad_light.fig +++ b/doc/quad_light.fig @@ -7,6 +7,11 @@ Letter Single -2 1200 2 +5 1 0 1 0 7 50 -1 -1 0.000 0 0 1 0 3204.068 1745.932 3150 1080 3678 1275 3870 1800 + 1 1 1.00 60.00 135.00 +2 1 0 3 0 7 50 -1 -1 0.000 0 0 7 1 0 2 + 1 1 2.00 120.00 270.00 + 3150 1800 5400 1800 2 1 0 3 0 7 50 -1 -1 0.000 0 0 7 1 0 2 1 1 2.00 120.00 270.00 3150 1800 3150 450 @@ -14,7 +19,9 @@ Single 3150 450 5400 450 5400 1800 3150 1800 3150 450 2 1 0 3 0 7 50 -1 -1 0.000 0 0 7 1 0 2 1 1 2.00 120.00 270.00 - 3150 1800 5400 1800 -4 0 0 50 -1 16 14 0.0000 4 240 675 3240 945 edge2\001 -4 0 0 50 -1 16 14 0.0000 4 240 675 4500 2115 edge1\001 + 3150 1800 3150 990 4 1 0 50 -1 16 14 0.0000 4 240 825 3105 2070 position\001 +4 0 0 50 -1 16 14 0.0000 4 240 675 3240 720 edge2\001 +4 2 0 50 -1 16 14 0.0000 4 240 675 5175 2070 edge1\001 +4 0 0 50 -1 16 14 0.0000 4 180 255 3240 1305 c0\001 +4 0 0 50 -1 16 14 0.0000 4 180 390 3915 1710 c90\001 diff --git a/modules/cpu/CMakeLists.txt b/modules/cpu/CMakeLists.txt index 96b2b28e1e..bb5c5e52e6 100644 --- a/modules/cpu/CMakeLists.txt +++ b/modules/cpu/CMakeLists.txt @@ -1,4 +1,4 @@ -## Copyright 2009-2021 Intel Corporation +## Copyright 2009-2022 Intel Corporation ## SPDX-License-Identifier: Apache-2.0 cmake_dependent_option( @@ -105,6 +105,7 @@ set(OSPRAY_ISPC_DEVICE_SOURCES lights/DirectionalLight.ispc lights/PointLight.cpp lights/PointLight.ispc + lights/IntensityDistribution.cpp lights/SpotLight.cpp lights/SpotLight.ispc lights/QuadLight.cpp diff --git a/modules/cpu/lights/AmbientLight.cpp b/modules/cpu/lights/AmbientLight.cpp index 2eea6e1a0e..bbb9d49dcd 100644 --- a/modules/cpu/lights/AmbientLight.cpp +++ b/modules/cpu/lights/AmbientLight.cpp @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "AmbientLight.h" @@ -35,8 +35,8 @@ void AmbientLight::processIntensityQuantityType() } else if (intensityQuantity == OSP_INTENSITY_QUANTITY_RADIANCE) { radiance = coloredIntensity; } else { - static WarnOnce warning( - "Unsupported intensityQuantity type for a 'ambient' light source"); + postStatusMsg(OSP_LOG_WARNING) + << toString() << " unsupported 'intensityQuantity' value"; radiance = vec3f(0.0f); } } diff --git a/modules/cpu/lights/DirectionalLight.cpp b/modules/cpu/lights/DirectionalLight.cpp index 156b77ef7f..d3cb0c08e5 100644 --- a/modules/cpu/lights/DirectionalLight.cpp +++ b/modules/cpu/lights/DirectionalLight.cpp @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "DirectionalLight.h" @@ -46,8 +46,8 @@ void DirectionalLight::processIntensityQuantityType() } else if (intensityQuantity == OSP_INTENSITY_QUANTITY_IRRADIANCE) { irradiance = coloredIntensity; } else { - static WarnOnce warning( - "Unsupported intensityQuantity type for a 'distant' light source"); + postStatusMsg(OSP_LOG_WARNING) + << toString() << " unsupported 'intensityQuantity' value"; irradiance = vec3f(0.0f); } } diff --git a/modules/cpu/lights/HDRILight.cpp b/modules/cpu/lights/HDRILight.cpp index 5eba8da223..5ba1c3d8c8 100644 --- a/modules/cpu/lights/HDRILight.cpp +++ b/modules/cpu/lights/HDRILight.cpp @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "HDRILight.h" @@ -55,8 +55,8 @@ void HDRILight::processIntensityQuantityType() // validate the correctness of the light quantity type if (intensityQuantity != OSP_INTENSITY_QUANTITY_SCALE && intensityQuantity != OSP_INTENSITY_QUANTITY_RADIANCE) { - static WarnOnce warning( - "Unsupported intensityQuantity type for a 'hdri' light source"); + postStatusMsg(OSP_LOG_WARNING) + << toString() << " unsupported 'intensityQuantity' value"; coloredIntensity = vec3f(0.0f); } } diff --git a/modules/cpu/lights/IntensityDistribution.cpp b/modules/cpu/lights/IntensityDistribution.cpp new file mode 100644 index 0000000000..2256bbfea0 --- /dev/null +++ b/modules/cpu/lights/IntensityDistribution.cpp @@ -0,0 +1,33 @@ +// Copyright 2022 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#include "IntensityDistribution.h" + +namespace ospray { + +void IntensityDistribution::readParams(ManagedObject &obj) +{ + c0 = obj.getParam("c0", c0); + lid = obj.getParamDataT("intensityDistribution"); + if (lid) { + if (lid->numItems.z > 1) + throw std::runtime_error(obj.toString() + + " must have (at most 2D) 'intensityDistribution' array using the first two dimensions."); + size = vec2i(lid->numItems.x, lid->numItems.y); + if (size.x < 2) + throw std::runtime_error(obj.toString() + + " 'intensityDistribution' must have data for at least two gamma angles."); + if (!lid->compact()) { + postStatusMsg(OSP_LOG_WARNING) + << obj.toString() + << " does currently not support strides for 'intensityDistribution', copying data."; + + const auto data = new Data(OSP_FLOAT, lid->numItems); + data->copy(*lid, vec3ui(0)); + lid = &(data->as()); + data->refDec(); + } + } +} + +} // namespace ospray diff --git a/modules/cpu/lights/IntensityDistribution.h b/modules/cpu/lights/IntensityDistribution.h new file mode 100644 index 0000000000..abbc4e7a7a --- /dev/null +++ b/modules/cpu/lights/IntensityDistribution.h @@ -0,0 +1,30 @@ +// Copyright 2022 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "common/Data.h" + +namespace ospray { + +struct OSPRAY_SDK_INTERFACE IntensityDistribution +{ + void readParams(ManagedObject &); + + operator bool() const + { + return lid; + } + float *data() const + { + return lid ? lid->data() : nullptr; + } + + vec2i size{0}; + vec3f c0{1.f, 0.f, 0.f}; + + private: + Ref> lid; // luminous intensity distribution +}; + +} // namespace ospray diff --git a/modules/cpu/lights/IntensityDistribution.ih b/modules/cpu/lights/IntensityDistribution.ih new file mode 100644 index 0000000000..8f16ec13e1 --- /dev/null +++ b/modules/cpu/lights/IntensityDistribution.ih @@ -0,0 +1,52 @@ +// Copyright 2022 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "rkcommon/math/vec.ih" + +struct IntensityDistribution +{ + float *lid; // luminance intensity distribution + vec2i size; + vec2f scale; +}; + +inline float IntensityDistribution_eval( + const IntensityDistribution *uniform self, + const uniform vec3f &c0, + const uniform vec3f &c90, + float cosAngle, + const vec3f &dir /*normalized*/) +{ + const float u = acos(cosAngle) * self->scale.x; + const float u_frac = frac(u); + const int u0 = u; + const int u1 = (u0 + 2 >= self->size.x) ? self->size.x - 1 : u0 + 1; + + float intensity; + if (self->size.y == 1) { // 1D + intensity = lerp(u_frac, self->lid[u0], self->lid[u1]); + } else { // 2D + const float v = (atan2(dot(dir, c90), dot(dir, c0)) + pi) * self->scale.y; + const float v_frac = frac(v); + const int v0 = v; + const int v1 = (v0 + 1 >= self->size.y) ? 0 : v0 + 1; + const int r0 = v0 * self->size.x; + const int r1 = v1 * self->size.x; + const float li0 = lerp(u_frac, self->lid[r0 + u0], self->lid[r0 + u1]); + const float li1 = lerp(u_frac, self->lid[r1 + u0], self->lid[r1 + u1]); + intensity = lerp(v_frac, li0, li1); + } + return intensity; +} + +inline float IntensityDistribution_set(IntensityDistribution *uniform self, + float *uniform lid, + const uniform vec2i &size) +{ + self->lid = lid; + self->size = size; + self->scale = make_vec2f(nextafter((size.x - 1) * one_over_pi, -1.0f), + nextafter(self->size.y * one_over_two_pi, -1.0f)); +} diff --git a/modules/cpu/lights/PointLight.cpp b/modules/cpu/lights/PointLight.cpp index 870e8bb313..266bb691a7 100644 --- a/modules/cpu/lights/PointLight.cpp +++ b/modules/cpu/lights/PointLight.cpp @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "PointLight.h" @@ -15,7 +15,11 @@ void *PointLight::createIE(const void *instance) const (ispc::vec3f &)position, (ispc::vec3f &)radiance, (ispc::vec3f &)radIntensity, - radius); + radius, + intensityDistribution.data(), + (const ispc::vec2i &)intensityDistribution.size, + (ispc::vec3f &)direction, + (const ispc::vec3f &)intensityDistribution.c0); return ie; } @@ -30,7 +34,16 @@ void PointLight::commit() position = getParam("position", vec3f(0.f)); radius = getParam("radius", 0.f); - queryIntensityQuantityType(OSP_INTENSITY_QUANTITY_INTENSITY); + // per default perpendicular to direction + direction = getParam("direction", vec3f(0.f, 0.f, 1.f)); + intensityDistribution.c0 = std::abs(direction.x) < std::abs(direction.y) + ? vec3f(0.0f, direction.z, direction.y) + : vec3f(direction.z, 0.0f, direction.x); + intensityDistribution.readParams(*this); + + queryIntensityQuantityType(intensityDistribution + ? OSP_INTENSITY_QUANTITY_SCALE + : OSP_INTENSITY_QUANTITY_INTENSITY); processIntensityQuantityType(); } @@ -38,38 +51,39 @@ void PointLight::processIntensityQuantityType() { radIntensity = 0.0f; radiance = 0.0f; - float sphereArea = 0.0f; - if (radius > 0.0f) { - sphereArea = 4.0f * M_PI * radius * radius; - } + const float sphereArea = 4.0f * M_PI * radius * radius; // converting from the chosen intensity quantity type to radiance // (for r > 0) or radiative intensity (r = 0) - if (intensityQuantity == OSP_INTENSITY_QUANTITY_INTENSITY) { + if (intensityDistribution + ? intensityQuantity == OSP_INTENSITY_QUANTITY_SCALE + : intensityQuantity == OSP_INTENSITY_QUANTITY_INTENSITY) { radIntensity = coloredIntensity; if (radius > 0.0f) { - // the visible surface are of a sphere in one direction - // is equal to the surface area of a disk oriented to this - // direction + // the visible surface are of a sphere in one direction is equal + // to the surface area of a disk oriented to this direction radiance = coloredIntensity / (sphereArea / 4.0f); } - } else if (intensityQuantity == OSP_INTENSITY_QUANTITY_POWER) { - radIntensity = coloredIntensity / (4.0f * M_PI); - if (radius > 0.0f) { - radiance = coloredIntensity / (M_PI * sphereArea); + return; + } + if (!intensityDistribution) { + if (intensityQuantity == OSP_INTENSITY_QUANTITY_POWER) { + radIntensity = coloredIntensity / (4.0f * M_PI); + if (radius > 0.0f) + radiance = coloredIntensity / (M_PI * sphereArea); + return; } - } else if (intensityQuantity == OSP_INTENSITY_QUANTITY_RADIANCE) { - // a virtual point light has no surface area - // therefore radIntensity stays zero and radiance is only - // set if radius > 0 - if (radius > 0.0f) { - radiance = coloredIntensity; + if (intensityQuantity == OSP_INTENSITY_QUANTITY_RADIANCE) { + // a virtual point light has no surface area therefore + // radIntensity stays zero + if (radius > 0.0f) + radiance = coloredIntensity; + return; } - } else { - static WarnOnce warning( - "Unsupported intensityQuantity type for a 'point' light source"); - radiance = vec3f(0.0f); } + + postStatusMsg(OSP_LOG_WARNING) + << toString() << " unsupported 'intensityQuantity' value"; } } // namespace ospray diff --git a/modules/cpu/lights/PointLight.h b/modules/cpu/lights/PointLight.h index d262dfd06d..ec2a1ece47 100644 --- a/modules/cpu/lights/PointLight.h +++ b/modules/cpu/lights/PointLight.h @@ -1,8 +1,9 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once +#include "IntensityDistribution.h" #include "Light.h" namespace ospray { @@ -24,6 +25,8 @@ struct OSPRAY_SDK_INTERFACE PointLight : public Light float radius{0.f}; //!< Radius of SphereLight vec3f radiance{1.f}; //!< emitted radiance of the SphereLight vec3f radIntensity{0.f}; + IntensityDistribution intensityDistribution; + vec3f direction{0.f, 0.f, 1.f}; }; } // namespace ospray diff --git a/modules/cpu/lights/PointLight.ispc b/modules/cpu/lights/PointLight.ispc index cec146f559..2772e349b1 100644 --- a/modules/cpu/lights/PointLight.ispc +++ b/modules/cpu/lights/PointLight.ispc @@ -1,6 +1,7 @@ // Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 +#include "IntensityDistribution.ih" #include "Light.ih" #include "common/Instance.ih" #include "common/Intersect.ih" @@ -8,28 +9,49 @@ #include "math/LinearSpace.ih" #include "math/sampling.ih" -struct PointLight +// state that changes under transformations +struct PointLightDynamic { - Light super; //!< inherited light fields + vec3f position; + vec3f direction; + vec3f c0; // orientation for intensityDistribution: direction of the C0- + vec3f c90; // and the C90-(half)plane +}; - vec3f position; //!< light position - vec3f intensity; //!< RGB color and intensity of light - vec3f radiance; //!< emitted RGB radiance - float radius; //!< defines the size of the SphereLight +struct PointLight +{ + Light super; + vec3f intensity; // RGB color and intensity of light + vec3f radiance; // emitted RGB radiance + float radius; // defines the size of the SphereLight + IntensityDistribution intensityDistribution; + PointLightDynamic pre; // un- or pre-transformed state }; // Implementation ////////////////////////////////////////////////////////////////////////////// +inline void Transform(const PointLight *uniform self, + const uniform affine3f xfm, + uniform PointLightDynamic &dyn) +{ + dyn.position = xfmPoint(xfm, self->pre.position); + + if (self->intensityDistribution.lid) { + dyn.direction = normalize(xfmVector(xfm, self->pre.direction)); + dyn.c90 = normalize(cross(xfmVector(xfm, self->pre.c0), dyn.direction)); + dyn.c0 = cross(dyn.direction, dyn.c90); + } +} inline Light_SampleRes Sample(const PointLight *uniform self, - const uniform vec3f &pos, + const uniform PointLightDynamic &dyn, const DifferentialGeometry &dg, const vec2f &s) { Light_SampleRes res; // extant light vector from the hit point - const vec3f dir = pos - dg.P; + const vec3f dir = dyn.position - dg.P; const float dist2 = dot(dir, dir); const float invdist = rsqrt(dist2); @@ -39,8 +61,6 @@ inline Light_SampleRes Sample(const PointLight *uniform self, res.pdf = inf; // per default we always take this sample - // convert from intensity to radiance by attenuating by distance^2 - res.weight = self->intensity * sqr(invdist); const float sinTheta = self->radius * invdist; if ((self->radius > 0.f) & (sinTheta > 0.005f)) { @@ -62,7 +82,18 @@ inline Light_SampleRes Sample(const PointLight *uniform self, // emit only from the surface to the outside res.weight = make_vec3f(0.f); } + } else { + // convert from intensity to radiance by attenuating by distance^2 + res.weight = self->intensity * sqr(invdist); } + + if (self->intensityDistribution.lid) { + const float cosd = -dot(dyn.direction, res.dir); + res.weight = res.weight + * IntensityDistribution_eval( + &self->intensityDistribution, dyn.c0, dyn.c90, cosd, res.dir); + } + return res; } @@ -73,7 +104,7 @@ Light_SampleRes PointLight_sample(const Light *uniform super, { const PointLight *uniform self = (PointLight * uniform) super; assert(self); - return Sample(self, self->position, dg, s); + return Sample(self, self->pre, dg, s); } Light_SampleRes PointLight_sample_instanced(const Light *uniform super, @@ -90,13 +121,15 @@ Light_SampleRes PointLight_sample_instanced(const Light *uniform super, Light_SampleRes res; foreach_unique (utime in time) { const uniform affine3f xfm = Instance_getTransform(instance, utime); - res = Sample(self, xfmPoint(xfm, self->position), dg, s); + uniform PointLightDynamic dyn; + Transform(self, xfm, dyn); + res = Sample(self, dyn, dg, s); } return res; } inline Light_EvalRes Eval(const PointLight *uniform self, - const uniform vec3f &pos, + const uniform PointLightDynamic &dyn, const DifferentialGeometry &dg, const vec3f &dir, const float minDist, @@ -105,17 +138,24 @@ inline Light_EvalRes Eval(const PointLight *uniform self, Light_EvalRes res; res.radiance = make_vec3f(0.f); - const vec3f A = pos - dg.P; + const vec3f A = dyn.position - dg.P; const float centerDist2 = dot(A, A); const float sinTheta2 = sqr(self->radius) * rcp(centerDist2); if (and(self->radius > 0.f, sqrt(sinTheta2) > 0.005f)) { - const Intersections isect = intersectSphere(dg.P, dir, pos, self->radius); + const Intersections isect = + intersectSphere(dg.P, dir, dyn.position, self->radius); if (and(and(isect.entry.t > minDist, isect.entry.t < maxDist), isect.entry.hit)) { const float cosTheta = sqrt(1.f - sinTheta2); res.pdf = uniformSampleConePDF(cosTheta); res.radiance = self->radiance; + if (self->intensityDistribution.lid) { + const float cosAngle = -dot(dyn.direction, dir); + res.radiance = res.radiance + * IntensityDistribution_eval( + &self->intensityDistribution, dyn.c0, dyn.c90, cosAngle, dir); + } } } return res; @@ -130,7 +170,7 @@ Light_EvalRes PointLight_eval(const Light *uniform super, { const PointLight *uniform self = (PointLight * uniform) super; assert(self); - return Eval(self, self->position, dg, dir, minDist, maxDist); + return Eval(self, self->pre, dg, dir, minDist, maxDist); } Light_EvalRes PointLight_eval_instanced(const Light *uniform super, @@ -149,7 +189,9 @@ Light_EvalRes PointLight_eval_instanced(const Light *uniform super, Light_EvalRes res; foreach_unique (utime in time) { const uniform affine3f xfm = Instance_getTransform(instance, utime); - res = Eval(self, xfmPoint(xfm, self->position), dg, dir, minDist, maxDist); + uniform PointLightDynamic dyn; + Transform(self, xfm, dyn); + res = Eval(self, dyn, dg, dir, minDist, maxDist); } return res; } @@ -162,34 +204,54 @@ export void PointLight_set(void *uniform super, const uniform vec3f &position, const uniform vec3f &radiance, const uniform vec3f &intensity, - uniform float radius) + uniform float radius, + float *uniform lid, + const uniform vec2i &size, + const uniform vec3f &direction, + const uniform vec3f &c0) { - uniform PointLight *uniform self = (uniform PointLight * uniform) super; - self->position = position; + PointLight *uniform self = (PointLight * uniform) super; + self->radiance = radiance; self->intensity = intensity; self->radius = radius; + self->pre.position = position; + self->pre.direction = normalize(direction); + + IntensityDistribution_set(&self->intensityDistribution, lid, size); // Enable dynamic runtime instancing or apply static transformation const Instance *uniform instance = self->super.instance; if (instance) { + self->pre.c0 = c0; if (instance->motionBlur) { self->super.sample = PointLight_sample_instanced; self->super.eval = PointLight_eval_instanced; - } else { - self->position = xfmPoint(instance->xfm, self->position); - } + } else + Transform(self, instance->xfm, self->pre); + } else { + self->pre.c90 = normalize(cross(c0, self->pre.direction)); + self->pre.c0 = cross(self->pre.direction, self->pre.c90); } } //! Create an ispc-side PointLight object export void *uniform PointLight_create() { - uniform PointLight *uniform self = uniform new uniform PointLight; + PointLight *uniform self = uniform new PointLight; Light_Constructor(&self->super); self->super.sample = PointLight_sample; self->super.eval = PointLight_eval; - PointLight_set(self, make_vec3f(0.f), make_vec3f(1.f), make_vec3f(1.f), 0.f); + PointLight_set(self, + make_vec3f(0.f), + make_vec3f(1.f), + make_vec3f(1.f), + 0.f, + NULL, + make_vec2i(0), + make_vec3f(0.f, 1.f, 0.f), + make_vec3f(0.f, 0.f, 1.f)); + return self; } diff --git a/modules/cpu/lights/QuadLight.cpp b/modules/cpu/lights/QuadLight.cpp index 45d9560f97..1971f1cb7b 100644 --- a/modules/cpu/lights/QuadLight.cpp +++ b/modules/cpu/lights/QuadLight.cpp @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "QuadLight.h" @@ -15,7 +15,10 @@ void *QuadLight::createIE(const void *instance) const (ispc::vec3f &)radiance, (ispc::vec3f &)position, (ispc::vec3f &)edge1, - (ispc::vec3f &)edge2); + (ispc::vec3f &)edge2, + intensityDistribution.data(), + (const ispc::vec2i &)intensityDistribution.size, + (const ispc::vec3f &)intensityDistribution.c0); return ie; } @@ -31,25 +34,39 @@ void QuadLight::commit() edge1 = getParam("edge1", vec3f(1.f, 0.f, 0.f)); edge2 = getParam("edge2", vec3f(0.f, 1.f, 0.f)); - queryIntensityQuantityType(OSP_INTENSITY_QUANTITY_RADIANCE); + intensityDistribution.c0 = edge2; + intensityDistribution.readParams(*this); + + queryIntensityQuantityType(intensityDistribution + ? OSP_INTENSITY_QUANTITY_SCALE + : OSP_INTENSITY_QUANTITY_RADIANCE); processIntensityQuantityType(); } void QuadLight::processIntensityQuantityType() { - float quadArea = length(cross(edge1, edge2)); - /// converting from the chosen intensity quantity type to radiance - if (intensityQuantity == OSP_INTENSITY_QUANTITY_POWER) { - radiance = coloredIntensity / (M_PI * quadArea); - } else if (intensityQuantity == OSP_INTENSITY_QUANTITY_INTENSITY) { + const float quadArea = length(cross(edge1, edge2)); + + // converting from the chosen intensity quantity type to radiance + if (intensityDistribution + ? intensityQuantity == OSP_INTENSITY_QUANTITY_SCALE + : intensityQuantity == OSP_INTENSITY_QUANTITY_INTENSITY) { radiance = coloredIntensity / quadArea; - } else if (intensityQuantity == OSP_INTENSITY_QUANTITY_RADIANCE) { - radiance = coloredIntensity; - } else { - static WarnOnce warning( - "Unsupported intensityQuantity type for a 'quad' light source"); - radiance = vec3f(0.0f); + return; + } + if (!intensityDistribution) { + if (intensityQuantity == OSP_INTENSITY_QUANTITY_POWER) { + radiance = coloredIntensity / (M_PI * quadArea); + return; + } + if (intensityQuantity == OSP_INTENSITY_QUANTITY_RADIANCE) { + radiance = coloredIntensity; + return; + } } + postStatusMsg(OSP_LOG_WARNING) + << toString() << " unsupported 'intensityQuantity' value"; + radiance = vec3f(0.0f); } } // namespace ospray diff --git a/modules/cpu/lights/QuadLight.h b/modules/cpu/lights/QuadLight.h index f702b68691..64304459fc 100644 --- a/modules/cpu/lights/QuadLight.h +++ b/modules/cpu/lights/QuadLight.h @@ -1,8 +1,9 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once +#include "IntensityDistribution.h" #include "Light.h" namespace ospray { @@ -24,6 +25,7 @@ struct OSPRAY_SDK_INTERFACE QuadLight : public Light vec3f edge1{1.f, 0.f, 0.f}; //!< vectors to adjacent corners vec3f edge2{0.f, 1.f, 0.f}; //!< vectors to adjacent corners vec3f radiance{1.f}; //!< emitted radiance of the QuadLight + IntensityDistribution intensityDistribution; }; } // namespace ospray diff --git a/modules/cpu/lights/QuadLight.ispc b/modules/cpu/lights/QuadLight.ispc index 612f0ce77a..a66458e8fa 100644 --- a/modules/cpu/lights/QuadLight.ispc +++ b/modules/cpu/lights/QuadLight.ispc @@ -1,36 +1,61 @@ // Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 +#include "IntensityDistribution.ih" #include "Light.ih" #include "SphericalQuadSampling.ih" #include "common/Instance.ih" -struct QuadLight +// state that changes under transformations +struct QuadLightDynamic { - Light super; //!< inherited light fields - - vec3f radiance; //!< emitted RGB radiance - vec3f position; //!< world-space corner position of the light - vec3f edge1; //!< vectors to adjacent corners - vec3f edge2; //!< vectors to adjacent corners - - vec3f nnormal; //!< negated normal, the direction that the QuadLight is not - //!< emitting; normalized + vec3f position; + vec3f edge1; + vec3f edge2; + vec3f nnormal; // negated normal, the direction that the QuadLight is not + // emitting; normalized float ppdf; // probability to sample point on light = 1/area + vec3f c0; // orientation for intensityDistribution: direction of the C0- + vec3f c90; // and the C90-(half)plane +}; + +struct QuadLight +{ + Light super; + vec3f radiance; // emitted RGB radiance + IntensityDistribution intensityDistribution; + QuadLightDynamic pre; // un- or pre-transformed state }; // Implementation ////////////////////////////////////////////////////////////////////////////// -inline Light_SampleRes SampleCenter(const uniform vec3f &pos, - const uniform vec3f &edge1, - const uniform vec3f &edge2, - const uniform float ppdf, - const uniform vec3f &nnormal, - const uniform vec3f &radiance, - const DifferentialGeometry &dg) +inline void Transform(const QuadLight *uniform self, + const uniform affine3f xfm, + uniform QuadLightDynamic &dyn) +{ + // transform light into the world space + dyn.position = xfmPoint(xfm, self->pre.position); + dyn.edge1 = xfmVector(xfm, self->pre.edge1); + dyn.edge2 = xfmVector(xfm, self->pre.edge2); + + // calculate quad normal vector + const uniform vec3f ndirection = cross(dyn.edge2, dyn.edge1); + dyn.ppdf = rcp(length(ndirection)); // 1/area + dyn.nnormal = ndirection * dyn.ppdf; // normalize + + if (self->intensityDistribution.lid) { + dyn.c90 = normalize(cross(dyn.nnormal, xfmVector(xfm, self->pre.c0))); + dyn.c0 = cross(dyn.c90, dyn.nnormal); + } +} + +inline Light_SampleRes SampleArea(const QuadLight *uniform self, + const uniform QuadLightDynamic &dyn, + const DifferentialGeometry &dg, + const vec2f &s) { Light_SampleRes res; - const vec3f p = pos + edge1 * 0.5f + edge2 * 0.5f; + const vec3f p = dyn.position + dyn.edge1 * s.x + dyn.edge2 * s.y; // extant light vector from the hit point const vec3f dir = p - dg.P; @@ -41,37 +66,46 @@ inline Light_SampleRes SampleCenter(const uniform vec3f &pos, res.dist = dist; // convert to pdf wrt. solid angle - const float cosd = dot(nnormal, res.dir); - res.pdf = ppdf * sqr(dist) / abs(cosd); - - // emit only to one side - res.weight = cosd > 0.f ? radiance * rcp(res.pdf) : make_vec3f(0.f); + const float cosd = dot(dyn.nnormal, res.dir); + res.pdf = dyn.ppdf * sqr(dist) / abs(cosd); + + float weight; + if (self->intensityDistribution.lid) { + weight = IntensityDistribution_eval( + &self->intensityDistribution, dyn.c0, dyn.c90, cosd, res.dir); + // when an light distribution function is used we want to + // remove the cosine term. To avoid numerical issues + // at cosineAngle = 0 we use the fact that the division + // of radiance with the cosine cancels out. + weight /= dyn.ppdf * sqr(dist); + } else { + // emit only to one side + weight = cosd > 0.f ? rcp(res.pdf) : 0.f; + } + res.weight = self->radiance * weight; return res; } -inline Light_SampleRes Sample(const uniform vec3f &pos, - const uniform vec3f &edge1, - const uniform vec3f &edge2, - const uniform float ppdf, - const uniform vec3f &nnormal, - const uniform vec3f &radiance, +inline Light_SampleRes Sample(const QuadLight *uniform self, + const uniform QuadLightDynamic &dyn, const DifferentialGeometry &dg, const vec2f &s) { Light_SampleRes res; if (s.x == 0.0f && s.y == 0.0f) { // XXX SciVis - return SampleCenter(pos, edge1, edge2, ppdf, nnormal, radiance, dg); + vec2f ss = make_vec2f(0.5f); + return SampleArea(self, dyn, dg, ss); } // create spherical quad for solid angle sampling - SphericalQuad quad = - SphericalQuad_create(pos, edge1, edge2, neg(nnormal), dg.P); + SphericalQuad quad = SphericalQuad_create( + dyn.position, dyn.edge1, dyn.edge2, neg(dyn.nnormal), dg.P); // bilinear warped cosine weight approximation - const vec3f cosW = - computeCosineWeightedRNG(pos, edge1, edge2, dg.P, dg.Ng, s); + const vec3f cosW = computeCosineWeightedRNG( + dyn.position, dyn.edge1, dyn.edge2, dg.P, dg.Ng, s); // sample quad const vec3f dir = sampleSphericalQuad(quad, make_vec2f(cosW.x, cosW.y)); @@ -82,12 +116,28 @@ inline Light_SampleRes Sample(const uniform vec3f &pos, res.dist = dist; // convert to pdf wrt. solid angle - const float cosd = dot(nnormal, res.dir); + const float cosd = dot(dyn.nnormal, res.dir); res.pdf = quad.S == 0.f ? 0.f : cosW.z / quad.S; - // emit only to one side - res.weight = (cosd > 0.f) && (res.pdf != 0.f) ? radiance * rcp(res.pdf) - : make_vec3f(0.f); + if (self->intensityDistribution.lid) { + if (abs(cosd) < 0.005f) // handle numerical edge case + return SampleArea(self, dyn, dg, s); + else { + float weight = IntensityDistribution_eval( + &self->intensityDistribution, dyn.c0, dyn.c90, cosd, res.dir); + // when an light distribution function is used we want to + // remove the cosine term. To avoid numerical issues + // at cosineAngle = 0 we use the fact that the division + // of radiance with the cosine cancels out. + weight /= abs(cosd) * res.pdf; + res.weight = res.pdf != 0.f ? self->radiance * weight : make_vec3f(0.f); + } + } else { + // emit only to one side + res.weight = (cosd > 0.f) && (res.pdf != 0.f) + ? self->radiance * rcp(res.pdf) + : make_vec3f(0.f); + } return res; } @@ -99,14 +149,7 @@ Light_SampleRes QuadLight_sample(const Light *uniform super, { const QuadLight *uniform self = (QuadLight * uniform) super; assert(self); - return Sample(self->position, - self->edge1, - self->edge2, - self->ppdf, - self->nnormal, - self->radiance, - dg, - sp); + return Sample(self, self->pre, dg, sp); } Light_SampleRes QuadLight_sample_instanced(const Light *uniform super, @@ -123,29 +166,15 @@ Light_SampleRes QuadLight_sample_instanced(const Light *uniform super, Light_SampleRes res; foreach_unique (utime in time) { const uniform affine3f xfm = Instance_getTransform(instance, utime); - - // transform light into the world space - const uniform vec3f pos = xfmPoint(xfm, self->position); - const uniform vec3f edge1 = xfmVector(xfm, self->edge1); - const uniform vec3f edge2 = xfmVector(xfm, self->edge2); - - // calculate quad normal vector - const uniform vec3f ndirection = cross(edge2, edge1); - const uniform float ppdf = rcp(length(ndirection)); // 1/area - const uniform vec3f nnormal = ndirection * ppdf; // normalize - - // sample light - res = Sample(pos, edge1, edge2, ppdf, nnormal, self->radiance, dg, sp); + uniform QuadLightDynamic dyn; + Transform(self, xfm, dyn); + res = Sample(self, dyn, dg, sp); } return res; } inline Light_EvalRes Eval(const QuadLight *uniform self, - const uniform vec3f &pos, - const uniform vec3f &edge1, - const uniform vec3f &edge2, - const uniform float ppdf, - const uniform vec3f &nnormal, + const uniform QuadLightDynamic &dyn, const DifferentialGeometry &dg, const vec3f &dir, const float minDist, @@ -155,29 +184,37 @@ inline Light_EvalRes Eval(const QuadLight *uniform self, res.radiance = make_vec3f(0.f); // backfacing? - const float cosd = dot(nnormal, dir); + const float cosd = dot(dyn.nnormal, dir); // denominator = dot(cross(edge1, edge2), dir) == cosd/ppdf - if (cosd <= 0.f) + if (cosd <= 0.f && !self->intensityDistribution.lid) return res; - const vec3f c = pos - dg.P; + const vec3f c = dyn.position - dg.P; const vec3f r = cross(c, dir); - const float u = dot(r, edge1); - const float v = -dot(r, edge2); + const float rcosd = rcp(cosd); + const float u = dot(r, dyn.edge1) * rcosd; + const float v = -dot(r, dyn.edge2) * rcosd; // u/denominator > 1? - if (min(u, v) < 0.f | max(u, v) * ppdf > cosd) + if (min(u, v) < 0.f | max(u, v) * dyn.ppdf > 1.0f) return res; - const float rcosd = rcp(cosd); - const float dist = dot(nnormal, c) * rcosd; + const float dist = dot(dyn.nnormal, c) * rcosd; if (dist <= minDist | dist > maxDist) return res; - SphericalQuad quad = - SphericalQuad_create(pos, edge1, edge2, neg(nnormal), dg.P); + SphericalQuad quad = SphericalQuad_create( + dyn.position, dyn.edge1, dyn.edge2, neg(dyn.nnormal), dg.P); if (quad.S > 0.f) { res.radiance = self->radiance; + if (self->intensityDistribution.lid) { + // convert from intensity to radiance by canceling the the cosine + // term introduced by the Lambertian area light + res.radiance = res.radiance + * (IntensityDistribution_eval( + &self->intensityDistribution, dyn.c0, dyn.c90, cosd, dir) + / abs(cosd)); + } res.pdf = rcp(quad.S); } @@ -193,16 +230,7 @@ Light_EvalRes QuadLight_eval(const Light *uniform super, { const QuadLight *uniform self = (QuadLight * uniform) super; assert(self); - return Eval(self, - self->position, - self->edge1, - self->edge2, - self->ppdf, - self->nnormal, - dg, - dir, - minDist, - maxDist); + return Eval(self, self->pre, dg, dir, minDist, maxDist); } Light_EvalRes QuadLight_eval_instanced(const Light *uniform super, @@ -221,20 +249,9 @@ Light_EvalRes QuadLight_eval_instanced(const Light *uniform super, Light_EvalRes res; foreach_unique (utime in time) { const uniform affine3f xfm = Instance_getTransform(instance, utime); - - // transform light into the world space - const uniform vec3f pos = xfmPoint(xfm, self->position); - const uniform vec3f edge1 = xfmVector(xfm, self->edge1); - const uniform vec3f edge2 = xfmVector(xfm, self->edge2); - - // calculate quad normal vector - const uniform vec3f ndirection = cross(edge2, edge1); - const uniform float ppdf = rcp(length(ndirection)); // 1/area - const uniform vec3f nnormal = ndirection * ppdf; // normalize - - // evaluate light - res = - Eval(self, pos, edge1, edge2, ppdf, nnormal, dg, dir, minDist, maxDist); + uniform QuadLightDynamic dyn; + Transform(self, xfm, dyn); + res = Eval(self, dyn, dg, dir, minDist, maxDist); } return res; } @@ -242,41 +259,46 @@ Light_EvalRes QuadLight_eval_instanced(const Light *uniform super, // Exports (called from C++) ////////////////////////////////////////////////////////////////////////////// -//! Set the parameters of an ispc-side QuadLight object export void QuadLight_set(void *uniform super, const uniform vec3f &radiance, const uniform vec3f &position, const uniform vec3f &edge1, - const uniform vec3f &edge2) + const uniform vec3f &edge2, + float *uniform lid, + const uniform vec2i &size, + const uniform vec3f &c0) { - uniform QuadLight *uniform self = (uniform QuadLight * uniform) super; + QuadLight *uniform self = (QuadLight * uniform) super; self->radiance = radiance; - self->position = position; - self->edge1 = edge1; - self->edge2 = edge2; + + self->pre.position = position; + self->pre.edge1 = edge1; + self->pre.edge2 = edge2; + + IntensityDistribution_set(&self->intensityDistribution, lid, size); // Enable dynamic runtime instancing or apply static transformation const Instance *uniform instance = self->super.instance; if (instance) { + self->pre.c0 = c0; if (instance->motionBlur) { self->super.sample = QuadLight_sample_instanced; self->super.eval = QuadLight_eval_instanced; - } else { - self->position = xfmPoint(instance->xfm, self->position); - self->edge1 = xfmVector(instance->xfm, self->edge1); - self->edge2 = xfmVector(instance->xfm, self->edge2); - } + } else + Transform(self, instance->xfm, self->pre); + } else { + const uniform vec3f ndirection = cross(edge2, edge1); + self->pre.ppdf = rcp(length(ndirection)); // 1/area + self->pre.nnormal = ndirection * self->pre.ppdf; // normalize + self->pre.c90 = normalize(cross(self->pre.nnormal, c0)); + self->pre.c0 = cross(self->pre.c90, self->pre.nnormal); } - - const uniform vec3f ndirection = cross(self->edge2, self->edge1); - self->ppdf = rcp(length(ndirection)); // 1/area - self->nnormal = ndirection * self->ppdf; // normalize } //! Create an ispc-side QuadLight object export void *uniform QuadLight_create() { - uniform QuadLight *uniform self = uniform new uniform QuadLight; + QuadLight *uniform self = uniform new QuadLight; Light_Constructor(&self->super); self->super.sample = QuadLight_sample; @@ -286,6 +308,9 @@ export void *uniform QuadLight_create() make_vec3f(0.f), make_vec3f(0.f), make_vec3f(1.f, 0.f, 0.f), + make_vec3f(0.f, 1.f, 0.f), + NULL, + make_vec2i(0), make_vec3f(0.f, 1.f, 0.f)); return self; diff --git a/modules/cpu/lights/SpotLight.cpp b/modules/cpu/lights/SpotLight.cpp index 1c6d33f232..36bbd48505 100644 --- a/modules/cpu/lights/SpotLight.cpp +++ b/modules/cpu/lights/SpotLight.cpp @@ -14,15 +14,15 @@ void *SpotLight::createIE(const void *instance) const ispc::SpotLight_set(ie, (const ispc::vec3f &)position, (const ispc::vec3f &)direction, - (const ispc::vec3f &)c0, + (const ispc::vec3f &)intensityDistribution.c0, (const ispc::vec3f &)radiance, (const ispc::vec3f &)radIntensity, cosAngleMax, cosAngleScale, radius, innerRadius, - (const ispc::vec2i &)size, - lid ? lid->data() : nullptr); + (const ispc::vec2i &)intensityDistribution.size, + intensityDistribution.data()); return ie; } @@ -43,30 +43,10 @@ void SpotLight::commit() clamp(getParam("innerRadius", 0.f), 0.0f, 0.999f * radius); // per default perpendicular to direction - c0 = std::abs(direction.x) < std::abs(direction.y) + intensityDistribution.c0 = std::abs(direction.x) < std::abs(direction.y) ? vec3f(0.0f, direction.z, direction.y) : vec3f(direction.z, 0.0f, direction.x); - c0 = getParam("c0", c0); - lid = getParamDataT("intensityDistribution"); - if (lid) { - if (lid->numItems.z > 1) - throw std::runtime_error(toString() - + " must have (at most 2D) 'intensityDistribution' array using the first two dimensions."); - size = vec2i(lid->numItems.x, lid->numItems.y); - if (size.x < 2) - throw std::runtime_error(toString() - + " 'intensityDistribution' must have data for at least two gamma angles."); - if (!lid->compact()) { - postStatusMsg(OSP_LOG_WARNING) - << toString() - << " does currently not support strides for 'intensityDistribution', copying data."; - - const auto data = new Data(OSP_FLOAT, lid->numItems); - data->copy(*lid, vec3ui(0)); - lid = &(data->as()); - data->refDec(); - } - } + intensityDistribution.readParams(*this); // check ranges and pre-compute parameters openingAngle = clamp(openingAngle, 0.f, 360.f); @@ -77,7 +57,9 @@ void SpotLight::commit() std::cos(deg2rad(0.5f * openingAngle - penumbraAngle)); cosAngleScale = 1.0f / (cosAngleMin - cosAngleMax); - queryIntensityQuantityType(OSP_INTENSITY_QUANTITY_INTENSITY); + queryIntensityQuantityType(intensityDistribution + ? OSP_INTENSITY_QUANTITY_SCALE + : OSP_INTENSITY_QUANTITY_INTENSITY); processIntensityQuantityType(openingAngle); } @@ -85,45 +67,42 @@ void SpotLight::processIntensityQuantityType(const float openingAngle) { radIntensity = 0.0f; radiance = 0.0f; + const float halfOpeningAngleRad = M_PI * (openingAngle * 0.5f) / 180.0f; + const float cosHalfOpeningAngle = cos(halfOpeningAngleRad); + const auto sqr = [](const float f) { return f * f; }; + const float ringDiskArea = M_PI * (sqr(radius) - sqr(innerRadius)); + const float sphericalCapCosInt = M_PI * (1.0f - sqr(cosHalfOpeningAngle)); - float halfOpeningAngleRad = M_PI * (openingAngle * 0.5f) / 180.0f; - float cosHalfOpeningAngle = cos(halfOpeningAngleRad); - float ringDiskArea = M_PI * (radius * radius - innerRadius * innerRadius); - - float sphericalCapCosInt = - M_PI * (1.0f - cosHalfOpeningAngle * cosHalfOpeningAngle); - - if (intensityQuantity == OSP_INTENSITY_QUANTITY_INTENSITY) { + // converting from the chosen intensity quantity type to radiance + if (intensityDistribution + ? intensityQuantity == OSP_INTENSITY_QUANTITY_SCALE + : intensityQuantity == OSP_INTENSITY_QUANTITY_INTENSITY) { radIntensity = coloredIntensity; - if (radius > 0.0f) { + if (radius > 0.0f) radiance = radIntensity / ringDiskArea; + return; + } + if (!intensityDistribution) { + if (intensityQuantity == OSP_INTENSITY_QUANTITY_POWER) { + // since our spot light implementation includes the cosine term we need + // to consider the integrated cosine cap instead of the usually used + // integrated cap + radIntensity = coloredIntensity / sphericalCapCosInt; + if (radius > 0.0f) + radiance = coloredIntensity / (sphericalCapCosInt * ringDiskArea); + return; } - } else if (intensityQuantity == OSP_INTENSITY_QUANTITY_POWER) { - // since our spot light implementation includes the cosine term - // we need to consider the integrated cosine cap instead of the - // usually used integrated cap. - radIntensity = coloredIntensity / sphericalCapCosInt; - if (radius > 0.0f) { - radiance = coloredIntensity / (sphericalCapCosInt * ringDiskArea); - } - if (lid) { - static WarnOnce warning( - "The 'intensityQuantity' : 'OSP_INTENSITY_QUANTITY_POWER' is not supported when using an 'intensityDistribution'"); - radIntensity = 0.0f; - radiance = 0.0f; - } - } else if (intensityQuantity == OSP_INTENSITY_QUANTITY_RADIANCE) { - // a virtual spot light has no surface area - // therefore radIntensity stays zero and radiance is only - // set if radius > 0 - if (radius > 0.0f) { + } + // XXX support for RADIANCE with intensityDistribution is deprecated + if (intensityQuantity == OSP_INTENSITY_QUANTITY_RADIANCE) { + // a virtual spot light has no surface area therefore radIntensity stays 0 + if (radius > 0.0f) radiance = coloredIntensity; - } - } else { - static WarnOnce warning( - "Unsupported intensityQuantity type for a 'spot' light source"); - radiance = vec3f(0.0f); + return; } + + postStatusMsg(OSP_LOG_WARNING) + << toString() << " unsupported 'intensityQuantity' value"; } } // namespace ospray diff --git a/modules/cpu/lights/SpotLight.h b/modules/cpu/lights/SpotLight.h index 107b736d0a..79537b2110 100644 --- a/modules/cpu/lights/SpotLight.h +++ b/modules/cpu/lights/SpotLight.h @@ -1,10 +1,10 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once +#include "IntensityDistribution.h" #include "Light.h" -#include "common/Data.h" namespace ospray { @@ -21,17 +21,15 @@ struct OSPRAY_SDK_INTERFACE SpotLight : public Light private: void processIntensityQuantityType(const float openingAngle); - Ref> lid; // luminous intensity distribution - vec2i size{0}; vec3f position{0.f}; vec3f direction{0.f, 0.f, 1.f}; - vec3f c0{1.f, 0.f, 0.f}; vec3f radiance{1.f}; vec3f radIntensity{0.f}; float radius{0.f}; float innerRadius{0.f}; float cosAngleMax{1.f}; float cosAngleScale{1.f}; + IntensityDistribution intensityDistribution; }; } // namespace ospray diff --git a/modules/cpu/lights/SpotLight.ispc b/modules/cpu/lights/SpotLight.ispc index d5f3439702..0ee2385ab5 100644 --- a/modules/cpu/lights/SpotLight.ispc +++ b/modules/cpu/lights/SpotLight.ispc @@ -1,16 +1,23 @@ // Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 +#include "IntensityDistribution.ih" #include "Light.ih" #include "common/Instance.ih" #include "math/sampling.ih" +// state that changes under transformations +struct SpotLightDynamic +{ + vec3f position; + vec3f direction; + vec3f c0; + vec3f c90; +}; + struct SpotLight { Light super; - - vec3f position; // Position of the SpotLight - vec3f direction; vec3f intensity; // RGB radiative intensity of the SpotLight vec3f radiance; // emitted RGB radiance float cosAngleMax; // Angular limit of the spot in an easier to use form: @@ -22,67 +29,42 @@ struct SpotLight float innerRadius; // defines the size of the inner cut out of the DiskLight // to represent a RingLight float areaPdf; // pdf of disk/ring with radius/innerRadius - - float *lid; // luminance intensity distribution - vec2i size; - vec2f scale; - vec3f c0; // orientation of lid - vec3f c90; + IntensityDistribution intensityDistribution; + SpotLightDynamic pre; // un- or pre-transformed state }; // Implementation ////////////////////////////////////////////////////////////////////////////// -inline float AngularAttenuation(const SpotLight *uniform self, float cosAngle) +inline void Transform(const uniform SpotLightDynamic &pre, + const uniform affine3f xfm, + uniform SpotLightDynamic &dyn) { - return clamp((cosAngle - self->cosAngleMax) * self->cosAngleScale); + dyn.position = xfmPoint(xfm, pre.position); + dyn.direction = normalize(xfmVector(xfm, pre.direction)); + dyn.c90 = normalize(cross(xfmVector(xfm, pre.c0), dyn.direction)); + dyn.c0 = cross(dyn.direction, dyn.c90); } -inline float IntensityDistr(const SpotLight *uniform self, - float cosAngle, - const uniform vec3f &c0, - const uniform vec3f &c90, - const vec3f &dir) +inline float AngularAttenuation(const SpotLight *uniform self, float cosAngle) { - const float u = acos(cosAngle) * self->scale.x; - const float u_frac = frac(u); - const int u0 = u; - const int u1 = (u0 + 2 >= self->size.x) ? self->size.x - 1 : u0 + 1; - - float intensity; - if (self->size.y == 1) { // 1D - intensity = lerp(u_frac, self->lid[u0], self->lid[u1]); - } else { // 2D - const float v = (atan2(dot(dir, c90), dot(dir, c0)) + pi) * self->scale.y; - const float v_frac = frac(v); - const int v0 = v; - const int v1 = (v0 + 1 >= self->size.y) ? 0 : v0 + 1; - const int r0 = v0 * self->size.x; - const int r1 = v1 * self->size.x; - const float li0 = lerp(u_frac, self->lid[r0 + u0], self->lid[r0 + u1]); - const float li1 = lerp(u_frac, self->lid[r1 + u0], self->lid[r1 + u1]); - intensity = lerp(v_frac, li0, li1); - } - return intensity; + return clamp((cosAngle - self->cosAngleMax) * self->cosAngleScale); } inline Light_SampleRes Sample(const SpotLight *uniform self, - const uniform vec3f &pos, - const uniform vec3f &dir, - const uniform vec3f &c0, - const uniform vec3f &c90, + const uniform SpotLightDynamic &dyn, const DifferentialGeometry &dg, const vec2f &s) { Light_SampleRes res; // extant light vector from the hit point - res.dir = pos - dg.P; + res.dir = dyn.position - dg.P; if (self->radius > 0.0f) { uniform linear3f l2w; - l2w.vx = c0; - l2w.vy = c90; - l2w.vz = dir; + l2w.vx = dyn.c0; + l2w.vy = dyn.c90; + l2w.vz = dyn.direction; res.dir = l2w * uniformSampleRing(self->radius, self->innerRadius, s) + res.dir; } @@ -95,15 +77,16 @@ inline Light_SampleRes Sample(const SpotLight *uniform self, res.dist = dist2 * invdist; // cosine of the negated light direction and light vector. - const float cosAngle = -dot(dir, res.dir); + const float cosAngle = -dot(dyn.direction, res.dir); float weight = AngularAttenuation(self, cosAngle); - if (self->lid) - weight *= IntensityDistr(self, cosAngle, c0, c90, res.dir); + if (self->intensityDistribution.lid) + weight *= IntensityDistribution_eval( + &self->intensityDistribution, dyn.c0, dyn.c90, cosAngle, res.dir); if (self->radius > 0.0f) { // convert area PDF to solid angle PDF res.pdf = self->areaPdf * (dist2 / abs(cosAngle)); - if (self->lid) { + if (self->intensityDistribution.lid) { // when an light distribution function is used we want to // remove the cosine term. To avoid numerical issues // at cosineAngle = 0 we use the fact that the division @@ -115,7 +98,7 @@ inline Light_SampleRes Sample(const SpotLight *uniform self, res.weight = self->radiance * weight; } else { res.pdf = inf; // we always take this sample - if (!self->lid) { + if (!self->intensityDistribution.lid) { // if the spotlight does not use a measured LDF we // simulate Lambertian behavior by multiplication with cosineAngle weight *= abs(cosAngle); @@ -134,8 +117,7 @@ Light_SampleRes SpotLight_sample(const Light *uniform super, { const SpotLight *uniform self = (SpotLight * uniform) super; assert(self); - return Sample( - self, self->position, self->direction, self->c0, self->c90, dg, s); + return Sample(self, self->pre, dg, s); } Light_SampleRes SpotLight_sample_instanced(const Light *uniform super, @@ -152,24 +134,15 @@ Light_SampleRes SpotLight_sample_instanced(const Light *uniform super, Light_SampleRes res; foreach_unique (utime in time) { const uniform affine3f xfm = Instance_getTransform(instance, utime); - - // transform light into the world space - const uniform vec3f pos = xfmPoint(xfm, self->position); - const uniform vec3f dir = normalize(xfmVector(xfm, self->direction)); - const uniform vec3f c90 = normalize(cross(xfmVector(xfm, self->c0), dir)); - const uniform vec3f c0 = cross(dir, c90); - - // sample light - res = Sample(self, pos, dir, c0, c90, dg, s); + uniform SpotLightDynamic dyn; + Transform(self->pre, xfm, dyn); + res = Sample(self, dyn, dg, s); } return res; } inline Light_EvalRes Eval(const SpotLight *uniform self, - const uniform vec3f &position, - const uniform vec3f &direction, - const uniform vec3f &c0, - const uniform vec3f &c90, + const uniform SpotLightDynamic &dyn, const DifferentialGeometry &dg, const vec3f &dir, const float minDist, @@ -180,22 +153,21 @@ inline Light_EvalRes Eval(const SpotLight *uniform self, if (self->radius > 0.f) { // intersect ring - const float cosAngle = -dot(dir, direction); + const float cosAngle = -dot(dyn.direction, dir); if (cosAngle > self->cosAngleMax) { // inside illuminated cone? - const vec3f vp = dg.P - position; - const float t = dot(vp, direction) * rcp(cosAngle); + const vec3f vp = dg.P - dyn.position; + const float t = dot(vp, dyn.direction) * rcp(cosAngle); if (t > minDist & t <= maxDist) { const vec3f vd = vp + t * dir; const float d2 = dot(vd, vd); // inside ring? if (and(d2radius), d2> sqr(self->innerRadius))) { float attenuation = AngularAttenuation(self, cosAngle); - if (self->lid) { - attenuation *= IntensityDistr(self, cosAngle, c0, c90, dir); - - // since the light distribution function already includes - // the cosine term we remove it from the Lambertian area - // light + if (self->intensityDistribution.lid) { + attenuation *= IntensityDistribution_eval( + &self->intensityDistribution, dyn.c0, dyn.c90, cosAngle, dir); + // convert from intensity to radiance by canceling the the cosine + // term introduced by the Lambertian area light attenuation /= abs(cosAngle); } @@ -220,15 +192,7 @@ Light_EvalRes SpotLight_eval(const Light *uniform super, { const SpotLight *uniform self = (SpotLight * uniform) super; assert(self); - return Eval(self, - self->position, - self->direction, - self->c0, - self->c90, - dg, - dir, - minDist, - maxDist); + return Eval(self, self->pre, dg, dir, minDist, maxDist); } Light_EvalRes SpotLight_eval_instanced(const Light *uniform super, @@ -247,15 +211,9 @@ Light_EvalRes SpotLight_eval_instanced(const Light *uniform super, Light_EvalRes res; foreach_unique (utime in time) { const uniform affine3f xfm = Instance_getTransform(instance, utime); - - // transform light into the world space - const uniform vec3f position = xfmPoint(xfm, self->position); - const uniform vec3f direction = normalize(xfmVector(xfm, self->direction)); - const uniform vec3f c90 = - normalize(cross(xfmVector(xfm, self->c0), direction)); - const uniform vec3f c0 = cross(direction, c90); - - res = Eval(self, position, direction, c0, c90, dg, dir, minDist, maxDist); + uniform SpotLightDynamic dyn; + Transform(self->pre, xfm, dyn); + res = Eval(self, dyn, dg, dir, minDist, maxDist); } return res; } @@ -278,26 +236,25 @@ export void SpotLight_set(void *uniform super, { SpotLight *uniform self = (SpotLight * uniform) super; - self->position = position; - self->direction = normalize(direction); - self->c0 = c0; + self->pre.position = position; + self->pre.direction = normalize(direction); + + IntensityDistribution_set(&self->intensityDistribution, lid, size); // Enable dynamic runtime instancing or apply static transformation const Instance *uniform instance = self->super.instance; if (instance) { + self->pre.c0 = c0; if (instance->motionBlur) { self->super.sample = SpotLight_sample_instanced; self->super.eval = SpotLight_eval_instanced; - } else { - self->position = xfmPoint(instance->xfm, self->position); - self->direction = normalize(xfmVector(instance->xfm, self->direction)); - self->c0 = xfmVector(instance->xfm, c0); - } + } else + Transform(self->pre, instance->xfm, self->pre); + } else { + self->pre.c90 = normalize(cross(c0, self->pre.direction)); + self->pre.c0 = cross(self->pre.direction, self->pre.c90); } - self->c90 = normalize(cross(self->c0, self->direction)); - self->c0 = cross(self->direction, self->c90); - self->radiance = radiance; self->intensity = intensity; self->cosAngleMax = cosAngleMax; @@ -305,12 +262,6 @@ export void SpotLight_set(void *uniform super, self->radius = radius; self->innerRadius = innerRadius; self->areaPdf = uniformSampleRingPDF(radius, innerRadius); - self->lid = lid; - if (self->lid) { - self->size = size; - self->scale = make_vec2f(nextafter((size.x - 1) * one_over_pi, -1.0f), - nextafter(self->size.y * one_over_two_pi, -1.0f)); - } } export void *uniform SpotLight_create() diff --git a/modules/cpu/lights/SunSkyLight.cpp b/modules/cpu/lights/SunSkyLight.cpp index 56a254c1f4..3babbec918 100644 --- a/modules/cpu/lights/SunSkyLight.cpp +++ b/modules/cpu/lights/SunSkyLight.cpp @@ -1,4 +1,4 @@ -// Copyright 2020-2021 Intel Corporation +// Copyright 2020-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // ospray @@ -176,8 +176,8 @@ void SunSkyLight::processIntensityQuantityType() coloredIntensity = getParam("color", vec3f(1.f)); intensityScale = 0.025f * getParam("intensity", 1.0f); } else { - static WarnOnce warning( - "Unsupported intensityQuantity type for a 'sunSky' light source"); + postStatusMsg(OSP_LOG_WARNING) + << toString() << " unsupported 'intensityQuantity' value"; coloredIntensity = vec3f(0.0f); } } diff --git a/test_image_data/baseline/AVX2/PhotometricLight_Light_parameter_10.png.md5 b/test_image_data/baseline/AVX2/PhotometricLight_Light_parameter_10.png.md5 new file mode 100644 index 0000000000..88fa380349 --- /dev/null +++ b/test_image_data/baseline/AVX2/PhotometricLight_Light_parameter_10.png.md5 @@ -0,0 +1 @@ +897322dcba60a3738bb35a161f68a266 diff --git a/test_image_data/baseline/AVX2/PhotometricLight_Light_parameter_11.png.md5 b/test_image_data/baseline/AVX2/PhotometricLight_Light_parameter_11.png.md5 new file mode 100644 index 0000000000..53b114a70b --- /dev/null +++ b/test_image_data/baseline/AVX2/PhotometricLight_Light_parameter_11.png.md5 @@ -0,0 +1 @@ +7c7ebd0441e73b86455662920b3f5a04 diff --git a/test_image_data/baseline/AVX2/PhotometricLight_Light_parameter_4.png.md5 b/test_image_data/baseline/AVX2/PhotometricLight_Light_parameter_4.png.md5 new file mode 100644 index 0000000000..3877e6562a --- /dev/null +++ b/test_image_data/baseline/AVX2/PhotometricLight_Light_parameter_4.png.md5 @@ -0,0 +1 @@ +97fe194b2dcbd54d45e391449c514b4d diff --git a/test_image_data/baseline/AVX2/PhotometricLight_Light_parameter_5.png.md5 b/test_image_data/baseline/AVX2/PhotometricLight_Light_parameter_5.png.md5 new file mode 100644 index 0000000000..f910a423a7 --- /dev/null +++ b/test_image_data/baseline/AVX2/PhotometricLight_Light_parameter_5.png.md5 @@ -0,0 +1 @@ +44328cc612a40a65c2a8e37db15d3e32 diff --git a/test_image_data/baseline/AVX2/PhotometricLight_Light_parameter_6.png.md5 b/test_image_data/baseline/AVX2/PhotometricLight_Light_parameter_6.png.md5 new file mode 100644 index 0000000000..0e04d03c2d --- /dev/null +++ b/test_image_data/baseline/AVX2/PhotometricLight_Light_parameter_6.png.md5 @@ -0,0 +1 @@ +63b0136d8b074c034affacc4b7545f45 diff --git a/test_image_data/baseline/AVX2/PhotometricLight_Light_parameter_7.png.md5 b/test_image_data/baseline/AVX2/PhotometricLight_Light_parameter_7.png.md5 new file mode 100644 index 0000000000..26765e2b27 --- /dev/null +++ b/test_image_data/baseline/AVX2/PhotometricLight_Light_parameter_7.png.md5 @@ -0,0 +1 @@ +7de1516a92e6baaa83a47efd60a53086 diff --git a/test_image_data/baseline/AVX2/PhotometricLight_Light_parameter_8.png.md5 b/test_image_data/baseline/AVX2/PhotometricLight_Light_parameter_8.png.md5 new file mode 100644 index 0000000000..09033a76ae --- /dev/null +++ b/test_image_data/baseline/AVX2/PhotometricLight_Light_parameter_8.png.md5 @@ -0,0 +1 @@ +2c562fee621fe1043fed6bc04d187d5d diff --git a/test_image_data/baseline/AVX2/PhotometricLight_Light_parameter_9.png.md5 b/test_image_data/baseline/AVX2/PhotometricLight_Light_parameter_9.png.md5 new file mode 100644 index 0000000000..2d8cb6247f --- /dev/null +++ b/test_image_data/baseline/AVX2/PhotometricLight_Light_parameter_9.png.md5 @@ -0,0 +1 @@ +089632b7f534d6b787f719c935c55a36 diff --git a/test_image_data/baseline/AVX512SKX/PhotometricLight_Light_parameter_10.png.md5 b/test_image_data/baseline/AVX512SKX/PhotometricLight_Light_parameter_10.png.md5 new file mode 100644 index 0000000000..88fa380349 --- /dev/null +++ b/test_image_data/baseline/AVX512SKX/PhotometricLight_Light_parameter_10.png.md5 @@ -0,0 +1 @@ +897322dcba60a3738bb35a161f68a266 diff --git a/test_image_data/baseline/AVX512SKX/PhotometricLight_Light_parameter_11.png.md5 b/test_image_data/baseline/AVX512SKX/PhotometricLight_Light_parameter_11.png.md5 new file mode 100644 index 0000000000..53b114a70b --- /dev/null +++ b/test_image_data/baseline/AVX512SKX/PhotometricLight_Light_parameter_11.png.md5 @@ -0,0 +1 @@ +7c7ebd0441e73b86455662920b3f5a04 diff --git a/test_image_data/baseline/AVX512SKX/PhotometricLight_Light_parameter_4.png.md5 b/test_image_data/baseline/AVX512SKX/PhotometricLight_Light_parameter_4.png.md5 new file mode 100644 index 0000000000..3877e6562a --- /dev/null +++ b/test_image_data/baseline/AVX512SKX/PhotometricLight_Light_parameter_4.png.md5 @@ -0,0 +1 @@ +97fe194b2dcbd54d45e391449c514b4d diff --git a/test_image_data/baseline/AVX512SKX/PhotometricLight_Light_parameter_5.png.md5 b/test_image_data/baseline/AVX512SKX/PhotometricLight_Light_parameter_5.png.md5 new file mode 100644 index 0000000000..f910a423a7 --- /dev/null +++ b/test_image_data/baseline/AVX512SKX/PhotometricLight_Light_parameter_5.png.md5 @@ -0,0 +1 @@ +44328cc612a40a65c2a8e37db15d3e32 diff --git a/test_image_data/baseline/AVX512SKX/PhotometricLight_Light_parameter_6.png.md5 b/test_image_data/baseline/AVX512SKX/PhotometricLight_Light_parameter_6.png.md5 new file mode 100644 index 0000000000..0e04d03c2d --- /dev/null +++ b/test_image_data/baseline/AVX512SKX/PhotometricLight_Light_parameter_6.png.md5 @@ -0,0 +1 @@ +63b0136d8b074c034affacc4b7545f45 diff --git a/test_image_data/baseline/AVX512SKX/PhotometricLight_Light_parameter_7.png.md5 b/test_image_data/baseline/AVX512SKX/PhotometricLight_Light_parameter_7.png.md5 new file mode 100644 index 0000000000..26765e2b27 --- /dev/null +++ b/test_image_data/baseline/AVX512SKX/PhotometricLight_Light_parameter_7.png.md5 @@ -0,0 +1 @@ +7de1516a92e6baaa83a47efd60a53086 diff --git a/test_image_data/baseline/AVX512SKX/PhotometricLight_Light_parameter_8.png.md5 b/test_image_data/baseline/AVX512SKX/PhotometricLight_Light_parameter_8.png.md5 new file mode 100644 index 0000000000..09033a76ae --- /dev/null +++ b/test_image_data/baseline/AVX512SKX/PhotometricLight_Light_parameter_8.png.md5 @@ -0,0 +1 @@ +2c562fee621fe1043fed6bc04d187d5d diff --git a/test_image_data/baseline/AVX512SKX/PhotometricLight_Light_parameter_9.png.md5 b/test_image_data/baseline/AVX512SKX/PhotometricLight_Light_parameter_9.png.md5 new file mode 100644 index 0000000000..2d8cb6247f --- /dev/null +++ b/test_image_data/baseline/AVX512SKX/PhotometricLight_Light_parameter_9.png.md5 @@ -0,0 +1 @@ +089632b7f534d6b787f719c935c55a36 From 31264b66eb3d9f1c693fd3fb5f6d7006e29eca7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20G=C3=BCnther?= Date: Tue, 1 Feb 2022 11:47:10 +0100 Subject: [PATCH 22/26] Refactor cylinder light --- modules/cpu/lights/CylinderLight.cpp | 6 +- modules/cpu/lights/CylinderLight.h | 10 +- modules/cpu/lights/CylinderLight.ispc | 129 ++++++++++++-------------- 3 files changed, 66 insertions(+), 79 deletions(-) diff --git a/modules/cpu/lights/CylinderLight.cpp b/modules/cpu/lights/CylinderLight.cpp index f049fb5eb6..6a0fea271c 100644 --- a/modules/cpu/lights/CylinderLight.cpp +++ b/modules/cpu/lights/CylinderLight.cpp @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "CylinderLight.h" @@ -54,8 +54,8 @@ void CylinderLight::processIntensityQuantityType() } else if (intensityQuantity == OSP_INTENSITY_QUANTITY_RADIANCE) { radiance = coloredIntensity; } else { - static WarnOnce warning( - "Unsupported intensityQuantity type for a 'cylinder' light source"); + postStatusMsg(OSP_LOG_WARNING) + << toString() << " unsupported 'intensityQuantity' value"; } } diff --git a/modules/cpu/lights/CylinderLight.h b/modules/cpu/lights/CylinderLight.h index 671abe7b0e..a06e28aa53 100644 --- a/modules/cpu/lights/CylinderLight.h +++ b/modules/cpu/lights/CylinderLight.h @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once @@ -20,10 +20,10 @@ struct OSPRAY_SDK_INTERFACE CylinderLight : public Light private: void processIntensityQuantityType(); - vec3f position0{0.f}; //!< world-space bottom position of the light - vec3f position1{0.f, 0.f, 1.f}; //!< world-space top position of the light - vec3f radiance{1.0f, 1.0f, 1.0f}; //!< emitted radiance of the CylinderLight - float radius{1.f}; //!< vector along the radius of the cylinder + vec3f position0{0.f}; // start of the cylinder + vec3f position1{0.f, 0.f, 1.f}; // end of the cylinder + vec3f radiance{1.0f, 1.0f, 1.0f}; + float radius{1.f}; }; } // namespace ospray diff --git a/modules/cpu/lights/CylinderLight.ispc b/modules/cpu/lights/CylinderLight.ispc index bbe38247f5..95c54d49ec 100644 --- a/modules/cpu/lights/CylinderLight.ispc +++ b/modules/cpu/lights/CylinderLight.ispc @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "../math/AffineSpace.ih" @@ -8,18 +8,31 @@ #include "common/Instance.ih" #include "common/Intersect.ih" -struct CylinderLight +// state that changes under transformations +struct CylinderLightDynamic { - Light super; - vec3f position0; vec3f position1; +}; + +struct CylinderLight +{ + Light super; vec3f radiance; float radius; + CylinderLightDynamic pre; // un- or pre-transformed state }; // Implementation ////////////////////////////////////////////////////////////////////////////// +inline void Transform(const CylinderLight *uniform self, + const uniform affine3f xfm, + uniform CylinderLightDynamic &dyn) +{ + dyn.position0 = xfmPoint(xfm, self->pre.position0); + dyn.position1 = xfmPoint(xfm, self->pre.position1); +} + inline float cylinderAxialSolidAngle( float c, float b, float h0, float h1, float b2c2) { @@ -113,12 +126,10 @@ inline void cylinderSampleSolidAngle(const DifferentialGeometry &dg, pdf = cosW.z; } -inline Light_SampleRes Sample(const DifferentialGeometry &dg, - const vec2f &sp, - const uniform vec3f &position0, - const uniform vec3f &position1, - const uniform float radius, - const uniform vec3f &radiance) +inline Light_SampleRes Sample(const CylinderLight *uniform self, + const uniform CylinderLightDynamic &dyn, + const DifferentialGeometry &dg, + const vec2f &sp) { Light_SampleRes res; res.weight = make_vec3f(0.f); @@ -127,16 +138,17 @@ inline Light_SampleRes Sample(const DifferentialGeometry &dg, float S, pdf; vec3f dir; if (sp.x == 0.0f && sp.y == 0.0f) { // XXX SciVis - dir = 0.5f * (position0 + position1) - dg.P; // to center + dir = 0.5f * (dyn.position0 + dyn.position1) - dg.P; // to center vec3f q0, e0, e1; cylinderComputeRectBounds( - position0, position1, radius, dg.P, q0, e0, e1, S); + dyn.position0, dyn.position1, self->radius, dg.P, q0, e0, e1, S); if (S > 0.f) { pdf = 1.f; } } else { - cylinderSampleSolidAngle(dg, sp, position0, position1, radius, dir, pdf, S); + cylinderSampleSolidAngle( + dg, sp, dyn.position0, dyn.position1, self->radius, dir, pdf, S); } if (S <= 0.f || pdf == 0.f) { @@ -147,11 +159,11 @@ inline Light_SampleRes Sample(const DifferentialGeometry &dg, // rejection sampling of the cylinder sample direction (through out cap hits // and rare misses) - Intersections isect = - intersectCylinder(dg.P, res.dir, position0, position1, radius); + Intersections isect = intersectCylinder( + dg.P, res.dir, dyn.position0, dyn.position1, self->radius); if (isect.entry.hit) { res.pdf = pdf / S; - res.weight = radiance * rcp(res.pdf); + res.weight = self->radiance * rcp(res.pdf); res.dist = isect.entry.t; } @@ -160,18 +172,17 @@ inline Light_SampleRes Sample(const DifferentialGeometry &dg, Light_SampleRes CylinderLight_sample(const uniform Light *uniform super, const DifferentialGeometry &dg, - const vec2f &sp, + const vec2f &s, const float) { const CylinderLight *uniform self = (CylinderLight * uniform) super; assert(self); - return Sample( - dg, sp, self->position0, self->position1, self->radius, self->radiance); + return Sample(self, self->pre, dg, s); } Light_SampleRes CylinderLight_sample_instanced(const Light *uniform super, const DifferentialGeometry &dg, - const vec2f &sp, + const vec2f &s, const float time) { const CylinderLight *uniform self = (CylinderLight * uniform) super; @@ -183,41 +194,35 @@ Light_SampleRes CylinderLight_sample_instanced(const Light *uniform super, Light_SampleRes res; foreach_unique (utime in time) { const uniform affine3f xfm = Instance_getTransform(instance, utime); - - // transform light into the world space - const uniform vec3f position0 = xfmPoint(xfm, self->position0); - const uniform vec3f position1 = xfmPoint(xfm, self->position1); - - // sample light - res = Sample(dg, sp, position0, position1, self->radius, self->radiance); + uniform CylinderLightDynamic dyn; + Transform(self, xfm, dyn); + res = Sample(self, dyn, dg, s); } return res; } -inline Light_EvalRes Eval(const DifferentialGeometry &dg, +inline Light_EvalRes Eval(const CylinderLight *uniform self, + const uniform CylinderLightDynamic &dyn, + const DifferentialGeometry &dg, const vec3f &dir, const float minDist, - const float maxDist, - const uniform vec3f &position0, - const uniform vec3f &position1, - const uniform float radius, - const uniform vec3f &radiance) + const float maxDist) { Light_EvalRes res; res.radiance = make_vec3f(0.f); // check if intersecting cylinder Intersections isect = - intersectCylinder(dg.P, dir, position0, position1, radius); + intersectCylinder(dg.P, dir, dyn.position0, dyn.position1, self->radius); if (isect.entry.hit) { if ((minDist < isect.entry.t && isect.entry.t <= maxDist)) { // compute bounding rectangle solid angle vec3f q0, e0, e1; float S; cylinderComputeRectBounds( - position0, position1, radius, dg.P, q0, e0, e1, S); + dyn.position0, dyn.position1, self->radius, dg.P, q0, e0, e1, S); if (S > 0.f) { - res.radiance = radiance; + res.radiance = self->radiance; res.pdf = rcp(S); } } @@ -233,16 +238,9 @@ Light_EvalRes CylinderLight_eval(const uniform Light *uniform super, const float maxDist, const float) { - uniform CylinderLight *uniform self = (uniform CylinderLight * uniform) super; + CylinderLight *uniform self = (CylinderLight * uniform) super; assert(self); - return Eval(dg, - dir, - minDist, - maxDist, - self->position0, - self->position1, - self->radius, - self->radiance); + return Eval(self, self->pre, dg, dir, minDist, maxDist); } Light_EvalRes CylinderLight_eval_instanced(const Light *uniform super, @@ -261,20 +259,9 @@ Light_EvalRes CylinderLight_eval_instanced(const Light *uniform super, Light_EvalRes res; foreach_unique (utime in time) { const uniform affine3f xfm = Instance_getTransform(instance, utime); - - // transform light into the world space - const uniform vec3f position0 = xfmPoint(xfm, self->position0); - const uniform vec3f position1 = xfmPoint(xfm, self->position1); - - // evaluate light - res = Eval(dg, - dir, - minDist, - maxDist, - position0, - position1, - self->radius, - self->radiance); + uniform CylinderLightDynamic dyn; + Transform(self, xfm, dyn); + res = Eval(self, dyn, dg, dir, minDist, maxDist); } return res; } @@ -288,32 +275,32 @@ export void CylinderLight_set(void *uniform super, const uniform vec3f &position1, const uniform float radius) { - uniform CylinderLight *uniform self = (uniform CylinderLight * uniform) super; - self->radiance = make_vec3f(0.f); + CylinderLight *uniform self = (CylinderLight * uniform) super; uniform float zMax = length(position1 - position0); - if (and(zMax > 0.f, radius > 0.f)) { - self->radiance = radiance; - self->radius = radius; - self->position0 = position0; - self->position1 = position1; + if (zMax <= 0.f | radius <= 0.f) { + self->radiance = make_vec3f(0.f); + return; } + self->radiance = radiance; + self->radius = radius; + self->pre.position0 = position0; + self->pre.position1 = position1; + // Enable dynamic runtime instancing or apply static transformation const Instance *uniform instance = self->super.instance; if (instance) { if (instance->motionBlur) { self->super.sample = CylinderLight_sample_instanced; self->super.eval = CylinderLight_eval_instanced; - } else { - self->position0 = xfmPoint(instance->xfm, position0); - self->position1 = xfmPoint(instance->xfm, position1); - } + } else + Transform(self, instance->xfm, self->pre); } } export void *uniform CylinderLight_create() { - uniform CylinderLight *uniform self = uniform new uniform CylinderLight; + CylinderLight *uniform self = uniform new CylinderLight; Light_Constructor(&self->super); self->super.sample = CylinderLight_sample; From 28ca5a96f10c77849fae3f4b48bfffc9ee6e910a Mon Sep 17 00:00:00 2001 From: David E DeMarle Date: Thu, 10 Feb 2022 16:27:14 +0000 Subject: [PATCH 23/26] refactor mpidevice to use OSPRayDevice instead of vkl/embree Removes vkl/embree devices and add an ospray device. Delegates setParam and new* to internal device to let it do that it needs to. Remove createLocalObject helpers. They appear to be a legacy that are not needed today. Ensure subdevice has a valid embree device for distributed world to use. Also clang-format touched files. --- modules/cpu/ISPCDevice.h | 7 +- modules/mpi/common/ObjectHandle.cpp | 2 +- modules/mpi/ospray/MPIDistributedDevice.cpp | 225 +++++++------------- modules/mpi/ospray/MPIDistributedDevice.h | 3 +- modules/mpi/ospray/common/OSPWork.cpp | 28 ++- 5 files changed, 111 insertions(+), 154 deletions(-) diff --git a/modules/cpu/ISPCDevice.h b/modules/cpu/ISPCDevice.h index 86992ed47b..257d711886 100644 --- a/modules/cpu/ISPCDevice.h +++ b/modules/cpu/ISPCDevice.h @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once @@ -127,6 +127,11 @@ struct OSPRAY_SDK_INTERFACE ISPCDevice : public Device std::shared_ptr loadBalancer; + RTCDevice getEmbreeDevice() + { + return embreeDevice; + } + private: RTCDevice embreeDevice = nullptr; VKLDevice vklDevice = nullptr; diff --git a/modules/mpi/common/ObjectHandle.cpp b/modules/mpi/common/ObjectHandle.cpp index a31886ee94..b7a4cbc360 100644 --- a/modules/mpi/common/ObjectHandle.cpp +++ b/modules/mpi/common/ObjectHandle.cpp @@ -48,7 +48,7 @@ void ObjectHandle::freeObject() const { auto it = objectByHandle.find(i64); if (it != objectByHandle.end()) { - it->second->refDec(); + // let internal device handle this -> it->second->refDec(); objectByHandle.erase(it); } } diff --git a/modules/mpi/ospray/MPIDistributedDevice.cpp b/modules/mpi/ospray/MPIDistributedDevice.cpp index 6207f0a845..b74ef702f7 100644 --- a/modules/mpi/ospray/MPIDistributedDevice.cpp +++ b/modules/mpi/ospray/MPIDistributedDevice.cpp @@ -31,37 +31,58 @@ namespace mpi { // Helper functions /////////////////////////////////////////////////////// -using SetParamFcn = void(OSPObject, const char *, const void *); +using SetParamFcn = void(std::shared_ptr, + OSPObject, + const char *, + const void *m, + OSPDataType); template -static void setParamOnObject(OSPObject _obj, const char *p, const T &v) +static void setParamOnObject(std::shared_ptr d, + OSPObject _obj, + const char *p, + const T &v, + OSPDataType t) { auto *obj = lookupObject(_obj); - obj->setParam(p, v); + d->setObjectParam((OSPObject)obj, p, t, &v); } #define declare_param_setter(TYPE) \ { \ - OSPTypeFor::value, [](OSPObject o, const char *p, const void *v) { \ - setParamOnObject(o, p, *(TYPE *)v); \ - } \ + OSPTypeFor::value, \ + [](std::shared_ptr d, \ + OSPObject o, \ + const char *p, \ + const void *v, \ + OSPDataType t) { setParamOnObject(d, o, p, *(TYPE *)v, t); } \ } #define declare_param_setter_object(TYPE) \ { \ - OSPTypeFor::value, [](OSPObject o, const char *p, const void *v) { \ - auto *obj = lookupObject( \ - *reinterpret_cast(v)); \ - setParamOnObject(o, p, obj); \ - } \ + OSPTypeFor::value, \ + [](std::shared_ptr d, \ + OSPObject o, \ + const char *p, \ + const void *v, \ + OSPDataType t) { \ + auto *obj = lookupObject( \ + *reinterpret_cast(v)); \ + setParamOnObject(d, o, p, obj, t); \ + } \ } #define declare_param_setter_string(TYPE) \ { \ - OSPTypeFor::value, [](OSPObject o, const char *p, const void *v) { \ - const char *str = (const char *)v; \ - setParamOnObject(o, p, std::string(str)); \ - } \ + OSPTypeFor::value, \ + [](std::shared_ptr d, \ + OSPObject o, \ + const char *p, \ + const void *v, \ + OSPDataType t) { \ + const char *str = (const char *)v; \ + setParamOnObject(d, o, p, std::string(str), t); \ + } \ } static std::map> setParamFcns = { @@ -143,19 +164,6 @@ static std::map> setParamFcns = { #undef declare_param_setter -template -inline API_TYPE createLocalObject(const char *type) -{ - auto *instance = OSPRAY_TYPE::createInstance(type); - return (API_TYPE)instance; -} - -template <> -inline OSPVolume createLocalObject(const char *type) -{ - return (OSPVolume) new Volume(type); -} - template inline API_TYPE createDistributedObject(const char *type, ObjectHandle handle) { @@ -164,24 +172,13 @@ inline API_TYPE createDistributedObject(const char *type, ObjectHandle handle) return (API_TYPE)(int64)handle; } -static void embreeErrorFunc(void *, const RTCError code, const char *str) -{ - postStatusMsg() << "#osp: embree internal error " << code << " : " << str; - throw std::runtime_error("embree internal error '" + std::string(str) + "'"); -} +// MPIDistributedDevice definitions /////////////////////////////////////// -static void vklErrorFunc(void *, const VKLError code, const char *str) +MPIDistributedDevice::MPIDistributedDevice() { - postStatusMsg() << "#osp: Open VKL internal error " << code << " : " << str; - OSPError e = - (code > VKL_UNSUPPORTED_CPU) ? OSP_UNKNOWN_ERROR : (OSPError)code; - handleError(e, "Open VKL internal error '" + std::string(str) + "'"); + internalDevice = std::make_shared(); } -// MPIDistributedDevice definitions /////////////////////////////////////// - -MPIDistributedDevice::MPIDistributedDevice() {} - MPIDistributedDevice::~MPIDistributedDevice() { messaging::shutdown(); @@ -193,18 +190,15 @@ MPIDistributedDevice::~MPIDistributedDevice() // Silently move on if finalize fails } } +} - try { - if (embreeDevice) { - rtcReleaseDevice(embreeDevice); - } - } catch (...) { - // silently move on, sometimes a pthread mutex lock fails in Embree - } - - if (vklDevice) { - vklReleaseDevice(vklDevice); - } +static void internalDeviceErrorFunc( + void *, const OSPError code, const char *str) +{ + postStatusMsg() << "#OSPRay MPI InternalDevice: internal error " << code + << " : " << str; + throw std::runtime_error( + "OSPRay MPIInternalDevice internal error '" + std::string(str) + "'"); } void MPIDistributedDevice::commit() @@ -216,7 +210,10 @@ void MPIDistributedDevice::commit() } if (!initialized) { + internalDevice->error_fcn = internalDeviceErrorFunc; + int _ac = 1; + const char *_av[] = {"ospray_mpi_distributed_device"}; auto *setComm = @@ -228,55 +225,6 @@ void MPIDistributedDevice::commit() else mpicommon::worker = mpicommon::world; - if (!embreeDevice) { - // ------------------------------------------------------- - // initialize embree. (we need to do this here rather than in - // ospray::init() because in mpi-mode the latter is also called - // in the host-stubs, where it shouldn't. - // ------------------------------------------------------- - embreeDevice = rtcNewDevice(generateEmbreeDeviceCfg(*this).c_str()); - rtcSetDeviceErrorFunction(embreeDevice, embreeErrorFunc, nullptr); - RTCError erc = rtcGetDeviceError(embreeDevice); - if (erc != RTC_ERROR_NONE) { - // why did the error function not get called !? - postStatusMsg() << "#osp:init: embree internal error number " << erc; - throw std::runtime_error("failed to initialize Embree"); - } - } - - if (!vklDevice) { - vklLoadModule("cpu_device"); - - int cpu_width = ispc::MPIDistributedDevice_programCount(); - switch (cpu_width) { - case 4: - vklDevice = vklNewDevice("cpu_4"); - break; - case 8: - vklDevice = vklNewDevice("cpu_8"); - break; - case 16: - vklDevice = vklNewDevice("cpu_16"); - break; - default: - vklDevice = vklNewDevice("cpu"); - break; - } - - vklDeviceSetErrorCallback(vklDevice, vklErrorFunc, nullptr); - vklDeviceSetLogCallback( - vklDevice, - [](void *, const char *message) { - postStatusMsg(OSP_LOG_INFO) << message; - }, - nullptr); - - vklDeviceSetInt(vklDevice, "logLevel", logLevel); - vklDeviceSetInt(vklDevice, "numThreads", numThreads); - - vklCommitDevice(vklDevice); - } - initialized = true; auto OSPRAY_FORCE_COMPRESSION = @@ -290,6 +238,8 @@ void MPIDistributedDevice::commit() messaging::init(mpicommon::worker); maml::start(); } + + internalDevice->commit(); } OSPFrameBuffer MPIDistributedDevice::frameBufferCreate( @@ -327,23 +277,20 @@ void MPIDistributedDevice::resetAccumulation(OSPFrameBuffer _fb) OSPGroup MPIDistributedDevice::newGroup() { - ospray::Group *ret = new Group; - ret->setDevice(embreeDevice); - return (OSPGroup)ret; + return internalDevice->newGroup(); } OSPInstance MPIDistributedDevice::newInstance(OSPGroup _group) { - auto *group = (Group *)_group; - auto *instance = new Instance(group); - return (OSPInstance)instance; + return internalDevice->newInstance(_group); } OSPWorld MPIDistributedDevice::newWorld() { ObjectHandle handle = allocateHandle(); auto *instance = new DistributedWorld; - instance->setDevice(embreeDevice); + instance->setDevice( + ((ospray::api::ISPCDevice *)internalDevice.get())->getEmbreeDevice()); handle.assign(instance); return (OSPWorld)(int64)(handle); } @@ -351,7 +298,7 @@ OSPWorld MPIDistributedDevice::newWorld() box3f MPIDistributedDevice::getBounds(OSPObject _obj) { auto *obj = lookupObject(_obj); - return obj->getBounds(); + return internalDevice->getBounds((OSPObject)obj); } OSPData MPIDistributedDevice::newSharedData(const void *sharedData, @@ -359,19 +306,18 @@ OSPData MPIDistributedDevice::newSharedData(const void *sharedData, const vec3ul &numItems, const vec3l &byteStride) { - return (OSPData) new Data(sharedData, type, numItems, byteStride); + return internalDevice->newSharedData(sharedData, type, numItems, byteStride); } OSPData MPIDistributedDevice::newData(OSPDataType type, const vec3ul &numItems) { - return (OSPData) new Data(type, numItems); + return internalDevice->newData(type, numItems); } void MPIDistributedDevice::copyData( const OSPData source, OSPData destination, const vec3ul &destinationIndex) { - Data *dst = (Data *)destination; - dst->copy(*(Data *)source, destinationIndex); + internalDevice->copyData(source, destination, destinationIndex); } int MPIDistributedDevice::loadModule(const char *name) @@ -381,7 +327,7 @@ int MPIDistributedDevice::loadModule(const char *name) OSPImageOperation MPIDistributedDevice::newImageOp(const char *type) { - return createLocalObject(type); + return internalDevice->newImageOp(type); } OSPRenderer MPIDistributedDevice::newRenderer(const char *type) @@ -392,57 +338,45 @@ OSPRenderer MPIDistributedDevice::newRenderer(const char *type) OSPCamera MPIDistributedDevice::newCamera(const char *type) { - auto c = createLocalObject(type); - auto *cam = lookupObject(c); - cam->setDevice(embreeDevice); - return c; + return internalDevice->newCamera(type); } OSPVolume MPIDistributedDevice::newVolume(const char *type) { - auto v = createLocalObject(type); - auto *volume = lookupObject(v); - volume->setDevice(embreeDevice, vklDevice); - return v; + return internalDevice->newVolume(type); } OSPGeometry MPIDistributedDevice::newGeometry(const char *type) { - auto g = createLocalObject(type); - auto *geom = lookupObject(g); - geom->setDevice(embreeDevice); - return g; + return internalDevice->newGeometry(type); } OSPGeometricModel MPIDistributedDevice::newGeometricModel(OSPGeometry _geom) { auto *geom = lookupObject(_geom); - auto *instance = new GeometricModel(geom); - return (OSPGeometricModel)instance; + return internalDevice->newGeometricModel((OSPGeometry)geom); } OSPVolumetricModel MPIDistributedDevice::newVolumetricModel(OSPVolume _vol) { auto *volume = lookupObject(_vol); - auto *instance = new VolumetricModel(volume); - return (OSPVolumetricModel)instance; + return internalDevice->newVolumetricModel((OSPVolume)volume); } OSPMaterial MPIDistributedDevice::newMaterial( - const char *, const char *material_type) + const char *unused, const char *material_type) { - auto *instance = Material::createInstance(nullptr, material_type); - return (OSPMaterial)instance; + return internalDevice->newMaterial(unused, material_type); } OSPTransferFunction MPIDistributedDevice::newTransferFunction(const char *type) { - return createLocalObject(type); + return internalDevice->newTransferFunction(type); } OSPLight MPIDistributedDevice::newLight(const char *type) { - return createLocalObject(type); + return internalDevice->newLight(type); } OSPFuture MPIDistributedDevice::renderFrame(OSPFrameBuffer _fb, @@ -520,14 +454,13 @@ float MPIDistributedDevice::getProgress(OSPFuture _task) float MPIDistributedDevice::getTaskDuration(OSPFuture _task) { - auto *task = (Future *)_task; - return task->getTaskDuration(); + return internalDevice->getTaskDuration(_task); } float MPIDistributedDevice::getVariance(OSPFrameBuffer _fb) { auto *fb = lookupDistributedObject(_fb); - return fb->getVariance(); + return internalDevice->getVariance((OSPFrameBuffer)fb); } void MPIDistributedDevice::setObjectParam( @@ -537,28 +470,24 @@ void MPIDistributedDevice::setObjectParam( throw std::runtime_error("cannot set OSP_UNKNOWN parameter type"); if (type == OSP_BYTE || type == OSP_RAW) { - setParamOnObject(object, name, *(const byte_t *)mem); + setParamOnObject(internalDevice, object, name, *(const byte_t *)mem, type); return; } - setParamFcns[type](object, name, mem); + setParamFcns[type](internalDevice, object, name, mem, type); } void MPIDistributedDevice::removeObjectParam( OSPObject _object, const char *name) { auto *object = lookupObject(_object); - auto *existing = object->getParam(name, nullptr); - if (existing) { - existing->refDec(); - } - object->removeParam(name); + return internalDevice->removeObjectParam((OSPObject)object, name); } void MPIDistributedDevice::commit(OSPObject _object) { auto *object = lookupObject(_object); - object->commit(); + internalDevice->commit((OSPObject)object); } void MPIDistributedDevice::release(OSPObject _obj) @@ -587,7 +516,7 @@ void MPIDistributedDevice::retain(OSPObject _obj) OSPTexture MPIDistributedDevice::newTexture(const char *type) { - return createLocalObject(type); + return internalDevice->newTexture(type); } OSPPickResult MPIDistributedDevice::pick(OSPFrameBuffer _fb, diff --git a/modules/mpi/ospray/MPIDistributedDevice.h b/modules/mpi/ospray/MPIDistributedDevice.h index 4596641719..a17ea2bfda 100644 --- a/modules/mpi/ospray/MPIDistributedDevice.h +++ b/modules/mpi/ospray/MPIDistributedDevice.h @@ -158,8 +158,7 @@ struct MPIDistributedDevice : public api::Device bool initialized{false}; bool shouldFinalizeMPI{false}; - RTCDevice embreeDevice = nullptr; - VKLDevice vklDevice = nullptr; + std::shared_ptr internalDevice = nullptr; }; } // namespace mpi diff --git a/modules/mpi/ospray/common/OSPWork.cpp b/modules/mpi/ospray/common/OSPWork.cpp index 055b087851..08e4997b08 100644 --- a/modules/mpi/ospray/common/OSPWork.cpp +++ b/modules/mpi/ospray/common/OSPWork.cpp @@ -251,7 +251,14 @@ void newSharedData(OSPState &state, auto data = retrieveData(state, cmdBuf, fabric, format, numItems, nullptr); - state.objects[handle] = (OSPData)data; + // gives an opportunity to pass off to internal device(s) + auto forsubs = ospNewSharedData( + data->data(), format, numItems.x, 0, numItems.y, 0, numItems.z, 0); + auto subscopy = ospNewData(format, numItems.x, numItems.y, numItems.z); + ospCopyData(forsubs, subscopy); + ospCommit(subscopy); + ospRelease(forsubs); + state.objects[handle] = (OSPData)subscopy; state.appSharedData[handle] = data; } @@ -320,6 +327,21 @@ void commit(OSPState &state, Data *d = state.getSharedDataHandle(handle); if (d) { retrieveData(state, cmdBuf, fabric, d->type, d->numItems, d); + + auto subscopy = (OSPData)state.objects[handle]; + + // gives an opportunity to pass off to internal device(s) + auto forsubs = ospNewSharedData(d->data(), + d->type, + d->numItems.x, + 0, + d->numItems.y, + 0, + d->numItems.z, + 0); + ospCopyData(forsubs, subscopy); + ospCommit(subscopy); + ospRelease(forsubs); } ospCommit(state.objects[handle]); @@ -344,7 +366,9 @@ void release( } } - if (state.getSharedDataHandle(handle)) { + Data *appData = state.getSharedDataHandle(handle); + if (appData) { + appData->refDec(); state.appSharedData.erase(handle); } } From 3efa6f914689f5b62312ca7663a91bfc5c064b48 Mon Sep 17 00:00:00 2001 From: Bruce Cherniak Date: Wed, 2 Feb 2022 18:04:09 -0600 Subject: [PATCH 24/26] Fix GeometryLights for ShadowCatcher --- modules/cpu/render/pathtracer/ShadowCatcher.ispc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/cpu/render/pathtracer/ShadowCatcher.ispc b/modules/cpu/render/pathtracer/ShadowCatcher.ispc index 12db5efd26..ce7f964859 100644 --- a/modules/cpu/render/pathtracer/ShadowCatcher.ispc +++ b/modules/cpu/render/pathtracer/ShadowCatcher.ispc @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "render/pathtracer/PathStructs.ih" @@ -34,6 +34,7 @@ bool shadowCatcher(const PathContext &pathContext, if (dot(ray.dir, pathVertex.dg.Ng) >= 0.f) pathVertex.dg.Ns = pathVertex.dg.Ng = neg(pathVertex.dg.Ng); + pathVertex.dg.renderer = &pathContext.context->super; pathVertex.dg.P = ray.org + pathState.shadowCatcherDist * ray.dir; const float eps = calcEpsilon(pathVertex.dg.P, ray.dir, pathState.shadowCatcherDist); From ce42e06709e342dae31fc919acaad829d24a40ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20G=C3=BCnther?= Date: Tue, 8 Feb 2022 18:42:25 +0100 Subject: [PATCH 25/26] Online blending for shadow catcher --- modules/cpu/render/pathtracer/PathSampler.ispc | 4 ++-- modules/cpu/render/pathtracer/ShadowCatcher.ispc | 10 +++------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/modules/cpu/render/pathtracer/PathSampler.ispc b/modules/cpu/render/pathtracer/PathSampler.ispc index 20cb6ef57a..94b9605cd5 100644 --- a/modules/cpu/render/pathtracer/PathSampler.ispc +++ b/modules/cpu/render/pathtracer/PathSampler.ispc @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "render/pathtracer/GeometryLight.ih" @@ -128,7 +128,7 @@ void samplePath(const PathContext &pathContext, sample.z = ray.t; if (shadowCatcher(pathContext, pathState, pathVertex, ray, sample)) - break; + pathVertex.type = ENVIRONMENT; pathVertex.wo = neg(ray.dir); diff --git a/modules/cpu/render/pathtracer/ShadowCatcher.ispc b/modules/cpu/render/pathtracer/ShadowCatcher.ispc index ce7f964859..d244a2b5db 100644 --- a/modules/cpu/render/pathtracer/ShadowCatcher.ispc +++ b/modules/cpu/render/pathtracer/ShadowCatcher.ispc @@ -83,15 +83,11 @@ bool shadowCatcher(const PathContext &pathContext, // order of args important to filter NaNs (in case unshaded.X is zero) const vec3f ratio = min( pathState.throughput * shaded * rcp(unshaded), pathState.throughput); -#ifdef COLORED_SHADOW_HACK - const float rm = reduce_min(ratio); - sample.alpha = 1.0f - rm; - pathState.contribution = ratio - rm; -#else + // alpha blend-in black shadow - sample.alpha = 1.0f - luminance(ratio); + pathState.throughput = ratio; pathState.contribution = make_vec3f(0.f); -#endif + return true; } From bf877c9e365822f17a70230bb2d4383a26e278fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20G=C3=BCnther?= Date: Tue, 15 Feb 2022 11:01:05 +0100 Subject: [PATCH 26/26] Update dependencies and README --- CHANGELOG.md | 68 +- README.md | 211 +- apps/common/external/imgui/imconfig.h | 23 +- apps/common/external/imgui/imgui.cpp | 5776 +++++++++++------ apps/common/external/imgui/imgui.h | 1057 +-- apps/common/external/imgui/imgui_draw.cpp | 729 ++- apps/common/external/imgui/imgui_internal.h | 1160 ++-- apps/common/external/imgui/imgui_tables.cpp | 419 +- apps/common/external/imgui/imgui_widgets.cpp | 1778 +++-- apps/common/external/imgui/imstb_rectpack.h | 52 +- apps/common/external/imgui/imstb_textedit.h | 82 +- apps/common/external/imgui/imstb_truetype.h | 110 +- apps/common/external/stb_image/stb_image.h | 281 +- .../external/stb_image/stb_image_write.h | 80 +- cmake/compiler/ispc.cmake | 2 +- cmake/ospray_options.cmake | 6 +- doc/api.md | 10 +- doc/compilation.md | 4 +- scripts/superbuild/CMakeLists.txt | 58 +- .../dependencies/dep_benchmark.cmake | 6 +- 20 files changed, 7796 insertions(+), 4116 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bdd8262c99..35a53c1a9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,27 +2,37 @@ Version History --------------- ### Changes in v2.9.0: -- Add support for dynamic load balancing in Offload device + - Add support for multi-segment deformation motion blur for `mesh` geometry -- Moved ISPC module to `modules/` folder and renamed the module to - `cpu` -- Refactored CMake variable names for enabling app categories - - OSPRAY_APPS_BENCHMARK refactored to OSPRAY_ENABLE_APPS_BENCHMARK - - OSPRAY_APPS_EXAMPLES refactored to OSPRAY_ENABLE_APPS_EXAMPLES - - OSPRAY_APPS_TUTORIALS refactored to OSPRAY_ENABLE_APPS_TUTORIALS - - OSPRAY_APPS_TESTING refactored to OSPRAY_ENABLE_APPS_TESTING -- Changed from area sampling to solid angle sampling for quad lights - in the pathtracer -- Instances can now have the group object rebound via a parameter -- MPI: Resolve issue where framebuffers would be leaked in the MPI - offload device. -- MPI: Resolve possible race condition in assigning IDs to distributed - objects +- OSPRay now requires minimum Open VKL v1.2.0 to bring the following + improvements: + - Structured regular volumes support for cell-centered data via + the `cellCentered` parameter (vertex-centered remains the + default) + - Particle volumes ignore particles with zero radius +- Add support for dynamic load balancing in MPI Offload device - Support for photometric lights (e.g., IES or EULUMDAT) also for `sphere` and `quad` lights. When setting `intensityDistribution`, other values for `intensityQuantity` than `OSP_INTENSITY_QUANTITY_SCALE` are deprecated +- Changed CMake variables for enabling app categories: + - `OSPRAY_ENABLE_APPS_BENCHMARK` replaces `OSPRAY_APPS_BENCHMARK` + - `OSPRAY_ENABLE_APPS_EXAMPLES` replaces `OSPRAY_APPS_EXAMPLES` + - `OSPRAY_ENABLE_APPS_TUTORIALS` replaces `OSPRAY_APPS_TUTORIALS` + - `OSPRAY_ENABLE_APPS_TESTING` replaces `OSPRAY_APPS_TESTING` +- Improve sampling of quad lights in the pathtracer (solid angle + instead of area) +- Instances can now have the group object rebound via a parameter +- Fix leaking framebuffers in the MPI Offload device +- Resolve possible race condition in assigning IDs to distributed + objects in the MPI device +- Move ISPC module to `modules/` folder and renamed the module to + `cpu` +- Minimum version of rkcommon is 1.9.0, which brings the following + improvements: + - Add support for Intel oneAPI DPCPP compiler + - Fix memory leak ### Changes in v2.8.0: @@ -45,8 +55,8 @@ Version History - Workaround dynamic linking issue on Windows in MPI distributed rendering - Correctly initialize `renderFrame` progress -- Improved performance of data-parallel rendering for scenes with - a large number of regions +- Improved performance of data-parallel rendering for scenes with a + large number of regions - Expanded camera model support of the data-parallel renderer, data-parallel rendering can now use all the camera models supported by the SciVis renderer @@ -60,8 +70,8 @@ Version History - OSPRay can now be built for ARM64 CPUs with NEON (e.g., Apple M1) using the superbuild. Thus, new minimum versions are for ISPC 1.16.0, for Embree 3.13.1 and for rkcommon 1.7.0 -- OSPRay now requires minimum Open VKL v1.0.0 to bring the - following improvements: +- OSPRay now requires minimum Open VKL v1.0.0 to bring the following + improvements: - Configurable `background` values for all volume types (default `NaN`), defining region outside the volume domain - Better default sampling rate for scaled VDB volumes, improved @@ -106,23 +116,23 @@ Version History and other MPICH-ABI compatible MPI distributions. The Windows release is built against MPI provided in the Intel oneAPI HPC Toolkit -- OSPRay now requires minimum Open VKL v0.13.0 to bring the - following improvements: +- OSPRay now requires minimum Open VKL v0.13.0 to bring the following + improvements: - Support half precision float (fp16) voxel data in strutured volumes (regular and spherical) and VDB volume - Supporting tricubic filtering via `VKL_FILTER_TRICUBIC` filter for VDB volume - Fixed artifacts for isosurfaces of unstructured volumes - - Performance improvements for isosurfaces when multiple - isovalues are selected + - Performance improvements for isosurfaces when multiple isovalues + are selected - Better, adaptive sampling of AMR volumes - The `mpiOffload` and `mpiDistributed` devices now support picking. Picking in the distributed device will return the globally closest - object on the rank that owns that object. Other ranks will report - no hit -- Messages issued from ISPC code use the same reporting path as the C++ - code, thus now the whole OSPRay console output can be consistently - filtered with log levels + object on the rank that owns that object. Other ranks will report no + hit +- Messages issued from ISPC code use the same reporting path as the + C++ code, thus now the whole OSPRay console output can be + consistently filtered with log levels - Open VKL and Embree internal errors are now correctly mapped to their corresponding OSPRay errors - Fix behavior of committing the framebuffer in distributed rendering @@ -409,7 +419,7 @@ Version History MPI distributed rendering documentation - Log levels are now controlled with enums and named strings (where applicable) - - A new flag was also introduced which turns all OSP_LOG_WARNING + - A new flag was also introduced which turns all `OSP_LOG_WARNING` messages into errors, which are submitted to the error callback instead of the message callback - Any unused parameters an object ignores now emit a warning diff --git a/README.md b/README.md index 7156cad995..e03448b35c 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ OSPRay ====== -This is release v2.9.0 (devel) of IntelĀ® OSPRay. For changes and new -features see the [changelog](CHANGELOG.md). Visit http://www.ospray.org -for more information. +This is release v2.9.0 of IntelĀ® OSPRay. For changes and new features +see the [changelog](CHANGELOG.md). Visit http://www.ospray.org for more +information. OSPRay Overview =============== @@ -100,7 +100,7 @@ before you can build OSPRay you need the following prerequisites: `embree_DIR`. - OSPRay also heavily uses Intel [Open VKL](https://www.openvkl.org/), - installing version 1.0.1 or newer is required. If Open VKL is not + installing version 1.2.0 or newer is required. If Open VKL is not found by CMake its location can be hinted with the variable `openvkl_DIR`. @@ -351,9 +351,9 @@ Documentation ============= The following [API -documentation](http://www.sdvis.org/ospray/download/OSPRay_readme_devel.pdf "OSPRay Documentation") +documentation](http://www.sdvis.org/ospray/download/OSPRay_readme.pdf "OSPRay Documentation") of OSPRay can also be found as a [pdf -document](http://www.sdvis.org/ospray/download/OSPRay_readme_devel.pdf "OSPRay Documentation"). +document](http://www.sdvis.org/ospray/download/OSPRay_readme.pdf "OSPRay Documentation"). For a deeper explanation of the concepts, design, features and performance of OSPRay also have a look at the IEEE Vis 2016 paper @@ -398,7 +398,7 @@ OSPRay parses (and removes) its known command line parameters from your applicationā€™s `main` function. For an example see the [tutorial](#osptutorial). For possible error codes see section [Error Handling and Status Messages](#error-handling-and-status-messages). It -is important to note that the arguments passed to `ospInit()` are +is important to note that the arguments passed to `ospInit` are processed in order they are listed. The following parameters (which are prefixed by convention with ā€œ`--osp:`ā€) are understood: @@ -616,7 +616,7 @@ typedef void (*OSPStatusCallback)(void *userData, const char* messageText); which OSPRay will use to emit status messages. By default, OSPRay uses a callback which does nothing, so any output desired by an application will require that a callback is provided. Note that callbacks for C++ -`std::cout` and `std::cerr` can be alternatively set through `ospInit()` +`std::cout` and `std::cerr` can be alternatively set through `ospInit` or the `OSPRAY_LOG_OUTPUT` environment variable. Applications can clear either callback by passing `NULL` instead of an @@ -647,8 +647,8 @@ void ospShutdown(); This API call ensures that the current device is cleaned up appropriately. Due to static object allocation having non-deterministic -ordering, it is recommended that applications call `ospShutdown()` -before the calling application process terminates. +ordering, it is recommended that applications call `ospShutdown` before +the calling application process terminates. Objects ------- @@ -911,6 +911,7 @@ table below. | vec3f | gridOrigin | (0,0,0) | origin of the grid in object-space | | vec3f | gridSpacing | (1,1,1) | size of the grid cells in object-space | | OSPData | data | | the actual voxel 3D [data](#data) | +| bool | cellCentered | false | whether the data is provided per cell (as opposed to per vertex) | | int | filter | `OSP_VOLUME_FILTER_TRILINEAR` | filter used for reconstructing the field, also allowed is `OSP_VOLUME_FILTER_NEAREST` and `OSP_VOLUME_FILTER_TRICUBIC` | | int | gradientFilter | same as `filter` | filter used during gradient computations | | float | background | `NaN` | value that is used when sampling an undefined region outside the volume domain | @@ -920,7 +921,9 @@ Configuration parameters for structured regular volumes. The size of the volume is inferred from the size of the 3D array `data`, as is the type of the voxel values (currently supported are: `OSP_UCHAR`, `OSP_SHORT`, `OSP_USHORT`, `OSP_HALF`, `OSP_FLOAT`, and -`OSP_DOUBLE`). +`OSP_DOUBLE`). Data can be provided either per cell or per vertex (the +default), selectable via the `cellCentered` parameter (which will also +affect the computed bounding box). ### Structured Spherical Volume @@ -1227,7 +1230,7 @@ rendering-specific parameters (where more than one set may exist concurrently). To create a volume instance, call ``` cpp -OSPVolumetricModel ospNewVolumetricModel(OSPVolume volume); +OSPVolumetricModel ospNewVolumetricModel(OSPVolume); ``` The passed volume can be `NULL` as long as the volume to be used is @@ -1238,10 +1241,10 @@ used. | Type | Name | Default | Description | |:--------------------|:-----------------|--------:|:-------------------------------------------------------------------------------------------------------------------------------------| +| OSPVolume | volume | | optional [volume](#volumes) object this model references | | OSPTransferFunction | transferFunction | | [transfer function](#transfer-function) to use | | float | densityScale | 1.0 | makes volumes uniformly thinner or thicker | | float | anisotropy | 0.0 | anisotropy of the (Henyey-Greenstein) phase function in \[-1ā€“1\] ([path tracer](#path-tracer) only), default to isotropic scattering | -| OSPVolume | volume | | optional [volume](#volumes) object this model references | Parameters understood by VolumetricModel. @@ -1264,13 +1267,16 @@ A mesh consisting of either triangles or quads is created by calling `ospNewGeometry` with type string ā€œ`mesh`ā€. Once created, a mesh recognizes the following parameters: -| Type | Name | Description | -|:------------------------|:----------------|:------------------------------------------------------------------------------------| -| vec3f\[\] | vertex.position | [data](#data) array of vertex positions | -| vec3f\[\] | vertex.normal | [data](#data) array of vertex normals | -| vec4f\[\] / vec3f\[\] | vertex.color | [data](#data) array of vertex colors (linear RGBA/RGB) | -| vec2f\[\] | vertex.texcoord | [data](#data) array of vertex texture coordinates | -| vec3ui\[\] / vec4ui\[\] | index | [data](#data) array of (either triangle or quad) indices (into the vertex array(s)) | +| Type | Name | Description | +|:------------------------|:-----------------------|:-------------------------------------------------------------------------------------------------------------| +| vec3f\[\] | vertex.position | [data](#data) array of vertex positions, overridden by `motion.*` arrays | +| vec3f\[\] | vertex.normal | [data](#data) array of vertex normals, overridden by `motion.*` arrays | +| vec4f\[\] / vec3f\[\] | vertex.color | [data](#data) array of vertex colors (linear RGBA/RGB) | +| vec2f\[\] | vertex.texcoord | [data](#data) array of vertex texture coordinates | +| vec3ui\[\] / vec4ui\[\] | index | [data](#data) array of (either triangle or quad) indices (into the vertex array(s)) | +| vec3f\[\]\[\] | motion.vertex.position | [data](#data) array of vertex position arrays (uniformly distributed keys for deformation motion blur) | +| vec3f\[\]\[\] | motion.vertex.normal | [data](#data) array of vertex normal arrays (uniformly distributed keys for deformation motion blur) | +| box1f | time | time associated with first and last key in `motion.*` arrays (for deformation motion blur), default \[0, 1\] | Parameters defining a mesh geometry. @@ -1486,7 +1492,7 @@ representation, and applies either full-object or per-primitive color and material information. To create a geometric model, call ``` cpp -OSPGeometricModel ospNewGeometricModel(OSPGeometry geometry); +OSPGeometricModel ospNewGeometricModel(OSPGeometry); ``` The passed geometry can be `NULL` as long as the geometry to be used is @@ -1512,13 +1518,13 @@ with normals oriented outside clips everything whatā€™s inside. | Type | Name | Description | |:-----------------------------|:--------------|:----------------------------------------------------------------------------------------------------------------------------------------------------| +| OSPGeometry | geometry | optional [geometry](#geometries) object this model references | | OSPMaterial / uint32 | material | optional [material](#materials) applied to the geometry, may be an index into the `material` parameter on the [renderer](#renderers) (if it exists) | | vec4f | color | optional color assigned to the geometry (linear RGBA) | | OSPMaterial\[\] / uint32\[\] | material | optional [data](#data) array of (per-primitive) materials, may be an index into the `material` parameter on the renderer (if it exists) | | vec4f\[\] | color | optional [data](#data) array of (per-primitive) colors (linear RGBA) | | uint8\[\] | index | optional [data](#data) array of per-primitive indices into `color` and `material` | | bool | invertNormals | inverts all shading normals (Ns), default false | -| OSPGeometry | geometry | optional \[geometry\] object this model references | Parameters understood by GeometricModel. @@ -1550,17 +1556,47 @@ represented by a lightā€™s `intensity` parameter is set using differ between the different light sources (see documentation of each specific light source). -| Name | Description | -|:----------------------------------|:--------------------------------------------------------------------------------------------------------------------------------| -| OSP_INTENSITY_QUANTITY_POWER | the overall amount of light energy emitted by the light source into the scene, unit is W | -| OSP_INTENSITY_QUANTITY_INTENSITY | the overall amount of light emitted by the light in a given direction, unit is W/sr | -| OSP_INTENSITY_QUANTITY_RADIANCE | the amount of light emitted by a point on the light source in a given direction, unit is W/sr/m2 | -| OSP_INTENSITY_QUANTITY_IRRADIANCE | the amount of light arriving at a surface point, assuming the light is oriented towards to the surface, unit is W/m2 | -| OSP_INTENSITY_QUANTITY_SCALE | a linear scaling factor for light sources with a built-in quantity (e.g., `HDRI`, or `sunSky`). | +| Name | Description | +|:----------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------| +| OSP_INTENSITY_QUANTITY_POWER | the overall amount of light energy emitted by the light source into the scene, unit is W | +| OSP_INTENSITY_QUANTITY_INTENSITY | the overall amount of light emitted by the light in a given direction, unit is W/sr | +| OSP_INTENSITY_QUANTITY_RADIANCE | the amount of light emitted by a point on the light source in a given direction, unit is W/sr/m2 | +| OSP_INTENSITY_QUANTITY_IRRADIANCE | the amount of light arriving at a surface point, assuming the light is oriented towards to the surface, unit is W/m2 | +| OSP_INTENSITY_QUANTITY_SCALE | a linear scaling factor for light sources with a built-in quantity (e.g., `HDRI`, or `sunSky`, or when using `intensityDistribution`). | Types of radiometric quantities used to interpret a lightā€™s `intensity` parameter. +### Photometric Lights + +Measured light sources (IES, EULUMDAT, ā€¦) are supported by the `sphere`, +`spot`, and `quad` lights when setting an `intensityDistribution` +[data](#data) array to modulate the intensity per direction. The mapping +is using the C-Ī³ coordinate system (see also below figure): the values +of the first (or only) dimension of `intensityDistribution` are +uniformly mapped to Ī³ in \[0ā€“Ļ€\]; the first intensity value to 0, the +last value to Ļ€, thus at least two values need to be present. + +![C-Ī³ coordinate system for the mapping of `intensityDistribution` with +photometric lights.](https://ospray.github.io/images/c-gamma_coords.png) + +If the array has a second dimension then the intensities are not +rotational symmetric around the main direction (where angle Ī³ is zero), +but are accordingly mapped to the C-halfplanes in \[0ā€“2Ļ€\]; the first +ā€œrowā€ of values to 0 and 2Ļ€, the other rows such that they have uniform +distance to its neighbors. The orientation of the C0-plane is specified +via `c0`. + +| Type | Name | Description | +|:----------|:----------------------|:----------------------------------------------------------------------------------------------------------------------------------------------| +| float\[\] | intensityDistribution | luminous intensity distribution for photometric lights; can be 2D for asymmetric illumination; values are assumed to be uniformly distributed | +| vec3f | c0 | orientation, i.e., direction of the C0-(half)plane (only needed if illumination via `intensityDistribution` is asymmetric) | + +Special parameters for photometric lights. + +When using an `intensityDistribution` then the default and only valid +value for `intensityQuantity` is `OSP_INTENSITY_QUANTITY_SCALE`. + The following light types are supported by most OSPRay renderers. ### Directional Light / Distant Light @@ -1593,16 +1629,20 @@ The sphere light (or the special case point light) is a light emitting uniformly in all directions from the surface toward the outside. It does not emit any light toward the inside of the sphere. It is created by passing the type string ā€œ`sphere`ā€ to `ospNewLight`. The point light -supports `OSP_INTENSITY_QUANTITY_POWER`, -`OSP_INTENSITY_QUANTITY_INTENSITY` (default) and -`OSP_INTENSITY_QUANTITY_RADIANCE` as `intensityQuantity` parameter -value. In addition to the [general parameters](#lights) understood by -all lights the sphere light supports the following special parameters: +supports only `OSP_INTENSITY_QUANTITY_SCALE` when +`intensityDistribution` is set, or otherwise +`OSP_INTENSITY_QUANTITY_POWER`, `OSP_INTENSITY_QUANTITY_INTENSITY` (then +default) and `OSP_INTENSITY_QUANTITY_RADIANCE` as `intensityQuantity` +parameter value. In addition to the [general parameters](#lights) +understood by all lights and the [photometric +parameters](#photometric-lights) the sphere light supports the following +special parameters: -| Type | Name | Default | Description | -|:------|:---------|--------:|:-------------------------------| -| vec3f | position | (0,0,0) | the center of the sphere light | -| float | radius | 0 | the size of the sphere light | +| Type | Name | Default | Description | +|:------|:----------|--------:|:--------------------------------------------| +| vec3f | position | (0,0,0) | the center of the sphere light | +| float | radius | 0 | the size of the sphere light | +| vec3f | direction | (0,0,1) | main orientation of `intensityDistribution` | Special parameters accepted by the sphere light. @@ -1610,27 +1650,27 @@ Setting the radius to a value greater than zero will result in soft shadows when the renderer uses stochastic sampling (like the [path tracer](#path-tracer)). -### Spotlight / Photometric Light +### Spotlight / Ring Light The spotlight is a light emitting into a cone of directions. It is created by passing the type string ā€œ`spot`ā€ to `ospNewLight`. The -spotlight supports `OSP_INTENSITY_QUANTITY_POWER`, -`OSP_INTENSITY_QUANTITY_INTENSITY` (default) and -`OSP_INTENSITY_QUANTITY_RADIANCE` as `intensityQuantity` parameter -value. In addition to the [general parameters](#lights) understood by -all lights the spotlight supports the special parameters listed in the -table. - -| Type | Name | Default | Description | -|:----------|:----------------------|--------:|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| vec3f | position | (0,0,0) | the center of the spotlight | -| vec3f | direction | (0,0,1) | main emission direction of the spot | -| float | openingAngle | 180 | full opening angle (in degree) of the spot; outside of this cone is no illumination | -| float | penumbraAngle | 5 | size (angle in degree) of the ā€œpenumbraā€, the region between the rim (of the illumination cone) and full intensity of the spot; should be smaller than half of `openingAngle` | -| float | radius | 0 | the size of the spotlight, the radius of a disk with normal `direction` | -| float | innerRadius | 0 | in combination with `radius` turns the disk into a ring | -| float\[\] | intensityDistribution | | luminous intensity distribution for photometric lights; can be 2D for asymmetric illumination; values are assumed to be uniformly distributed | -| vec3f | c0 | | orientation, i.e., direction of the C0-(half)plane (only needed if illumination via `intensityDistribution` is asymmetric) | +spotlight supports only `OSP_INTENSITY_QUANTITY_SCALE` when +`intensityDistribution` is set, or otherwise +`OSP_INTENSITY_QUANTITY_POWER`, `OSP_INTENSITY_QUANTITY_INTENSITY` (then +default) and `OSP_INTENSITY_QUANTITY_RADIANCE` as `intensityQuantity` +parameter value. In addition to the [general parameters](#lights) +understood by all lights and the [photometric +parameters](#photometric-lights) the spotlight supports the special +parameters listed in the table. + +| Type | Name | Default | Description | +|:------|:--------------|--------:|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| vec3f | position | (0,0,0) | the center of the spotlight | +| vec3f | direction | (0,0,1) | main emission direction of the spot | +| float | openingAngle | 180 | full opening angle (in degree) of the spot; outside of this cone is no illumination | +| float | penumbraAngle | 5 | size (angle in degree) of the ā€œpenumbraā€, the region between the rim (of the illumination cone) and full intensity of the spot; should be smaller than half of `openingAngle` | +| float | radius | 0 | the size of the spotlight, the radius of a disk with normal `direction` | +| float | innerRadius | 0 | in combination with `radius` turns the disk into a ring | Special parameters accepted by the spotlight. @@ -1642,33 +1682,18 @@ shadows when the renderer uses stochastic sampling (like the [path tracer](#path-tracer)). Additionally setting the inner radius will result in a ring instead of a disk emitting the light. -Measured light sources (IES, EULUMDAT, ā€¦) are supported by providing an -`intensityDistribution` [data](#data) array to modulate the intensity -per direction. The mapping is using the C-Ī³ coordinate system (see also -below figure): the values of the first (or only) dimension of -`intensityDistribution` are uniformly mapped to Ī³ in \[0ā€“Ļ€\]; the first -intensity value to 0, the last value to Ļ€, thus at least two values need -to be present. If the array has a second dimension then the intensities -are not rotational symmetric around `direction`, but are accordingly -mapped to the C-halfplanes in \[0ā€“2Ļ€\]; the first ā€œrowā€ of values to 0 -and 2Ļ€, the other rows such that they have uniform distance to its -neighbors. The orientation of the C0-plane is specified via `c0`. A -combination of using an `intensityDistribution` and -`OSP_INTENSITY_QUANTITY_POWER` as `intensityQuantity` is not supported -at the moment. - -![C-Ī³ coordinate system for the mapping of `intensityDistribution` to -the spotlight.](https://ospray.github.io/images/spot_coords.png) - ### Quad Light The quad[5] light is a planar, procedural area light source emitting uniformly on one side into the half-space. It is created by passing the -type string ā€œ`quad`ā€ to `ospNewLight`. The quad light supports -`OSP_INTENSITY_QUANTITY_POWER`, `OSP_INTENSITY_QUANTITY_INTENSITY` and -`OSP_INTENSITY_QUANTITY_RADIANCE` (default) as `intensityQuantity` -parameter. In addition to the [general parameters](#lights) understood -by all lights the quad light supports the following special parameters: +type string ā€œ`quad`ā€ to `ospNewLight`. The quad light supports only +`OSP_INTENSITY_QUANTITY_SCALE` when `intensityDistribution` is set, or +otherwise `OSP_INTENSITY_QUANTITY_POWER`, +`OSP_INTENSITY_QUANTITY_INTENSITY` and `OSP_INTENSITY_QUANTITY_RADIANCE` +(then default) as `intensityQuantity` parameter. In addition to the +[general parameters](#lights) understood by all lights and the +[photometric parameters](#photometric-lights) the quad light supports +the following special parameters: | Type | Name | Default | Description | |:------|:---------|--------:|:-----------------------------------------| @@ -1682,6 +1707,7 @@ Special parameters accepted by the quad light. reader.](https://ospray.github.io/images/quad_light.png) The emission side is determined by the cross product of `edge1`Ɨ`edge2`. +which is also the main emission direction for `intensityDistribution`. Note that only renderers that use stochastic sampling (like the path tracer) will compute soft shadows from the quad light. Other renderers will just sample the center of the quad light, which results in hard @@ -1839,8 +1865,15 @@ via a transform. To create and instance call OSPInstance ospNewInstance(OSPGroup); ``` +The passed group can be `NULL` as long as the group to be instanced is +passed as a parameter. If both a group is specified on object creation +and as a parameter, the parameter value is used. If the parameter value +is later removed, the group object passed on object creation is again +used. + | Type | Name | Default | Description | |:-------------|:-------------------|---------:|:------------------------------------------------------------------------------------------------------------------------------------------------------| +| OSPGroup | group | | optional [group](#groups) object to be instanced | | affine3f | transform | identity | world-space transform for all attached geometries and volumes, overridden by `motion.*` arrays | | affine3f\[\] | motion.transform | | uniformly distributed world-space transforms | | vec3f\[\] | motion.scale | | uniformly distributed world-space scale, overridden by `motion.transform` | @@ -3011,13 +3044,6 @@ This is useful for applications to query exactly how long an asynchronous task executed without the overhead of measuring both task execution + synchronization by the calling application. -### Asynchronously Rendering and ospCommit() - -The use of either `ospRenderFrame` or `ospRenderFrame` requires that all -objects in the scene being rendered have been committed before rendering -occurs. If a call to `ospCommit()` happens while a frame is rendered, -the result is undefined behavior and should be avoided. - ### Synchronous Rendering For convenience in certain use cases, `ospray_util.h` provides a @@ -3037,6 +3063,13 @@ return ospGetVariance(fb) This version is closest to `ospRenderFrame` from OSPRay v1.x. +### Rendering and ospCommit + +The use of either `ospRenderFrame` or `ospRenderFrameBlocking` requires +that all objects in the scene being rendered have been committed before +rendering occurs. If a call to `ospCommit` happens while a frame is +rendered, the result is undefined behavior and should be avoided. + Distributed Rendering with MPI ============================== @@ -3109,6 +3142,10 @@ The `maxCommandBufferEntries`, `commandBufferSize`, and `OSPRAY_MPI_COMMAND_BUFFER_SIZE`, and `OSPRAY_MPI_MAX_INLINE_DATA_SIZE`, respectively. +The `mpiOffload` device uses a dynamic load balancer by default. If you +wish to use a static load balancer you can do so by setting the +`OSPRAY_STATIC_BALANCER` environment variable to 1. + The `mpiOffload` device does not support multiple init/shutdown cycles. Thus, to run `ospBenchmark` for this device make sure to exclude the init/shutdown test by passing `--benchmark_filter=-ospInit_ospShutdown` @@ -3526,7 +3563,7 @@ possible to copy the entire data set on to each rank, or to accelerate loading of a large model by leveraging a parallel file system. [1] For example, if OSPRay is in `~/Projects/ospray`, ISPC will also be -searched in `~/Projects/ispc-v1.16.1-linux`. +searched in `~/Projects/ispc-v1.17.0-linux`. [2] This file is usually in `${install_location}/[lib|lib64]/cmake/ospray-${version}/`. If CMake diff --git a/apps/common/external/imgui/imconfig.h b/apps/common/external/imgui/imconfig.h index 5544c42a27..c17189170a 100644 --- a/apps/common/external/imgui/imconfig.h +++ b/apps/common/external/imgui/imconfig.h @@ -33,22 +33,26 @@ //---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to avoid using soon-to-be obsolete function/names. //#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS +//#define IMGUI_DISABLE_OBSOLETE_KEYIO // 1.87: disable legacy io.KeyMap[]+io.KeysDown[] in favor io.AddKeyEvent(). This will be folded into IMGUI_DISABLE_OBSOLETE_FUNCTIONS in a few versions. //---- Disable all of Dear ImGui or don't implement standard windows. // It is very strongly recommended to NOT disable the demo windows during development. Please read comments in imgui_demo.cpp. //#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty. //#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty. Not recommended. -//#define IMGUI_DISABLE_METRICS_WINDOW // Disable metrics/debugger window: ShowMetricsWindow() will be empty. +//#define IMGUI_DISABLE_METRICS_WINDOW // Disable metrics/debugger and other debug tools: ShowMetricsWindow() and ShowStackToolWindow() will be empty. //---- Don't implement some functions to reduce linkage requirements. -//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. -//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] Don't implement default IME handler. Won't use and link with ImmGetContext/ImmSetCompositionWindow. +//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. (user32.lib/.a, kernel32.lib/.a) +//#define IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with Visual Studio] Implement default IME handler (require imm32.lib/.a, auto-link for Visual Studio, -limm32 on command-line for MinGW) +//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a) //#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, ime). //#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default). //#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf) //#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself. -//#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function. +//#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies) +//#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function. //#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions(). +//#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available //---- Include imgui_user.h at the end of imgui.h as a convenience //#define IMGUI_INCLUDE_IMGUI_USER_H @@ -60,7 +64,7 @@ //#define IMGUI_USE_WCHAR32 //---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version -// By default the embedded implementations are declared static and not available outside of imgui cpp files. +// By default the embedded implementations are declared static and not available outside of Dear ImGui sources files. //#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h" //#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h" //#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION @@ -70,6 +74,15 @@ // Requires 'stb_sprintf.h' to be available in the include path. Compatibility checks of arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by STB sprintf. // #define IMGUI_USE_STB_SPRINTF +//---- Use FreeType to build and rasterize the font atlas (instead of stb_truetype which is embedded by default in Dear ImGui) +// Requires FreeType headers to be available in the include path. Requires program to be compiled with 'misc/freetype/imgui_freetype.cpp' (in this repository) + the FreeType library (not provided). +// On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'. +//#define IMGUI_ENABLE_FREETYPE + +//---- Use stb_truetype to build and rasterize the font atlas (default) +// The only purpose of this define is if you want force compilation of the stb_truetype backend ALONG with the FreeType backend. +//#define IMGUI_ENABLE_STB_TRUETYPE + //---- Define constructor and implicit cast operators to convert back<>forth between your math types and ImVec2/ImVec4. // This will be inlined as part of ImVec2 and ImVec4 class declarations. /* diff --git a/apps/common/external/imgui/imgui.cpp b/apps/common/external/imgui/imgui.cpp index b02f265df9..efa69604c3 100644 --- a/apps/common/external/imgui/imgui.cpp +++ b/apps/common/external/imgui/imgui.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.80 +// dear imgui, v1.87 // (main code and documentation) // Help: @@ -11,15 +11,19 @@ // - FAQ http://dearimgui.org/faq // - Homepage & latest https://github.com/ocornut/imgui // - Releases & changelog https://github.com/ocornut/imgui/releases -// - Gallery https://github.com/ocornut/imgui/issues/3488 (please post your screenshots/video there!) +// - Gallery https://github.com/ocornut/imgui/issues/4451 (please post your screenshots/video there!) +// - Wiki https://github.com/ocornut/imgui/wiki (lots of good stuff there) // - Glossary https://github.com/ocornut/imgui/wiki/Glossary -// - Wiki https://github.com/ocornut/imgui/wiki // - Issues & support https://github.com/ocornut/imgui/issues +// Getting Started? +// - For first-time users having issues compiling/linking/running or issues loading fonts: +// please post in https://github.com/ocornut/imgui/discussions if you cannot find a solution in resources above. + // Developed by Omar Cornut and every direct or indirect contributors to the GitHub. // See LICENSE.txt for copyright and licensing details (standard MIT License). // This library is free but needs your support to sustain development and maintenance. -// Businesses: you can support continued development via invoiced technical support, maintenance and sponsoring contracts. Please reach out to "contact AT dearimgui.org". +// Businesses: you can support continued development via invoiced technical support, maintenance and sponsoring contracts. Please reach out to "contact AT dearimgui.com". // Individuals: you can support continued development via donations. See docs/README or web page. // It is recommended that you don't modify imgui.cpp! It will become difficult for you to update the library. @@ -66,6 +70,7 @@ CODE // [SECTION] STYLING // [SECTION] RENDER HELPERS // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!) +// [SECTION] INPUTS // [SECTION] ERROR CHECKING // [SECTION] LAYOUT // [SECTION] SCROLLING @@ -75,8 +80,10 @@ CODE // [SECTION] DRAG AND DROP // [SECTION] LOGGING/CAPTURING // [SECTION] SETTINGS +// [SECTION] VIEWPORTS // [SECTION] PLATFORM DEPENDENT HELPERS // [SECTION] METRICS/DEBUGGER WINDOW +// [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, STACK TOOL) */ @@ -121,7 +128,6 @@ CODE - CTRL+X,CTRL+C,CTRL+V to use OS clipboard/ - CTRL+Z,CTRL+Y to undo/redo. - ESCAPE to revert text to its original value. - - You can apply arithmetic operators +,*,/ on numerical values. Use +- to subtract (because - would set a negative value!) - Controls are automatically adjusted for OSX to match standard OSX text editing operations. - General Keyboard controls: enable with ImGuiConfigFlags_NavEnableKeyboard. - General Gamepad controls: enable with ImGuiConfigFlags_NavEnableGamepad. See suggested mappings in imgui.h ImGuiNavInput_ + download PNG/PSD at http://dearimgui.org/controls_sheets @@ -132,17 +138,17 @@ CODE READ FIRST ---------- - - Remember to read the FAQ (https://www.dearimgui.org/faq) - - Your code creates the UI, if your code doesn't run the UI is gone! The UI can be highly dynamic, there are no construction - or destruction steps, less superfluous data retention on your side, less state duplication, less state synchronization, less bugs. + - Remember to check the wonderful Wiki (https://github.com/ocornut/imgui/wiki) + - Your code creates the UI, if your code doesn't run the UI is gone! The UI can be highly dynamic, there are no construction or + destruction steps, less superfluous data retention on your side, less state duplication, less state synchronization, fewer bugs. - Call and read ImGui::ShowDemoWindow() for demo code demonstrating most features. - The library is designed to be built from sources. Avoid pre-compiled binaries and packaged versions. See imconfig.h to configure your build. - Dear ImGui is an implementation of the IMGUI paradigm (immediate-mode graphical user interface, a term coined by Casey Muratori). - You can learn about IMGUI principles at http://www.johno.se/book/imgui.html, http://mollyrocket.com/861 & more links in the FAQ. + You can learn about IMGUI principles at http://www.johno.se/book/imgui.html, http://mollyrocket.com/861 & more links in Wiki. - Dear ImGui is a "single pass" rasterizing implementation of the IMGUI paradigm, aimed at ease of use and high-performances. - For every application frame your UI code will be called only once. This is in contrast to e.g. Unity's own implementation of an IMGUI, + For every application frame, your UI code will be called only once. This is in contrast to e.g. Unity's implementation of an IMGUI, where the UI code is called multiple times ("multiple passes") from a single entry point. There are pros and cons to both approaches. - - Our origin are on the top-left. In axis aligned bounding boxes, Min = top-left, Max = bottom-right. + - Our origin is on the top-left. In axis aligned bounding boxes, Min = top-left, Max = bottom-right. - This codebase is also optimized to yield decent performances with typical "Debug" builds settings. - Please make sure you have asserts enabled (IM_ASSERT redirects to assert() by default, but can be redirected). If you get an assert, read the messages and comments around the assert. @@ -155,8 +161,8 @@ CODE HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI ---------------------------------------------- - - Overwrite all the sources files except for imconfig.h (if you have made modification to your copy of imconfig.h) - - Or maintain your own branch where you have imconfig.h modified as a top-most commit which you can regularly rebase over master. + - Overwrite all the sources files except for imconfig.h (if you have modified your copy of imconfig.h) + - Or maintain your own branch where you have imconfig.h modified as a top-most commit which you can regularly rebase over "master". - You can also use '#define IMGUI_USER_CONFIG "my_config_file.h" to redirect configuration to your own file. - Read the "API BREAKING CHANGES" section (below). This is where we list occasional API breaking changes. If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed @@ -171,12 +177,12 @@ CODE - Run and study the examples and demo in imgui_demo.cpp to get acquainted with the library. - In the majority of cases you should be able to use unmodified backends files available in the backends/ folder. - Add the Dear ImGui source files + selected backend source files to your projects or using your preferred build system. - It is recommended you build and statically link the .cpp files as part of your project and NOT as shared library (DLL). + It is recommended you build and statically link the .cpp files as part of your project and NOT as a shared library (DLL). - You can later customize the imconfig.h file to tweak some compile-time behavior, such as integrating Dear ImGui types with your own maths types. - When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them. - Dear ImGui never touches or knows about your GPU state. The only function that knows about GPU is the draw function that you provide. Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render" - phases of your own application. All rendering information are stored into command-lists that you will retrieve after calling ImGui::Render(). + phases of your own application. All rendering information is stored into command-lists that you will retrieve after calling ImGui::Render(). - Refer to the backends and demo applications in the examples/ folder for instruction on how to setup your code. - If you are running over a standard OS with a common graphics API, you should be able to use unmodified imgui_impl_*** files from the examples/ folder. @@ -184,7 +190,7 @@ CODE HOW A SIMPLE APPLICATION MAY LOOK LIKE -------------------------------------- EXHIBIT 1: USING THE EXAMPLE BACKENDS (= imgui_impl_XXX.cpp files from the backends/ folder). - The sub-folders in examples/ contains examples applications following this structure. + The sub-folders in examples/ contain examples applications following this structure. // Application init: create a dear imgui context, setup some options, load fonts ImGui::CreateContext(); @@ -234,11 +240,11 @@ CODE unsigned char* pixels = NULL; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); - // At this point you've got the texture data and you need to upload that your your graphic system: + // At this point you've got the texture data and you need to upload that to your graphic system: // After we have created the texture, store its pointer/identifier (_in whichever format your engine uses_) in 'io.Fonts->TexID'. // This will be passed back to your via the renderer. Basically ImTextureID == void*. Read FAQ for details about ImTextureID. MyTexture* texture = MyEngine::CreateTextureFromMemoryPixels(pixels, width, height, TEXTURE_TYPE_RGBA32) - io.Fonts->TexID = (void*)texture; + io.Fonts->SetTexID((void*)texture); // Application main loop while (true) @@ -248,12 +254,12 @@ CODE io.DeltaTime = 1.0f/60.0f; // set the time elapsed since the previous frame (in seconds) io.DisplaySize.x = 1920.0f; // set the current display width io.DisplaySize.y = 1280.0f; // set the current display height here - io.MousePos = my_mouse_pos; // set the mouse position - io.MouseDown[0] = my_mouse_buttons[0]; // set the mouse button states - io.MouseDown[1] = my_mouse_buttons[1]; + io.AddMousePosEvent(mouse_x, mouse_y); // update mouse position + io.AddMouseButtonEvent(0, mouse_b[0]); // update mouse button states + io.AddMouseButtonEvent(1, mouse_b[1]); // update mouse button states // Call NewFrame(), after this point you can use ImGui::* functions anytime - // (So you want to try calling NewFrame() as early as you can in your mainloop to be able to use Dear ImGui everywhere) + // (So you want to try calling NewFrame() as early as you can in your main loop to be able to use Dear ImGui everywhere) ImGui::NewFrame(); // Most of your application code here @@ -273,14 +279,14 @@ CODE // Shutdown ImGui::DestroyContext(); - To decide whether to dispatch mouse/keyboard inputs to Dear ImGui to the rest your application, + To decide whether to dispatch mouse/keyboard inputs to Dear ImGui to the rest of your application, you should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags! Please read the FAQ and example applications for details about this! HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE --------------------------------------------- - The backends in impl_impl_XXX.cpp files contains many working implementations of a rendering function. + The backends in impl_impl_XXX.cpp files contain many working implementations of a rendering function. void void MyImGuiRenderFunction(ImDrawData* draw_data) { @@ -288,6 +294,7 @@ CODE // TODO: Setup viewport covering draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize // TODO: Setup orthographic projection matrix cover draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize // TODO: Setup shader: vertex { float2 pos, float2 uv, u32 color }, fragment shader sample color from 1 texture, multiply by vertex color. + ImVec2 clip_off = draw_data->DisplayPos; for (int n = 0; n < draw_data->CmdListsCount; n++) { const ImDrawList* cmd_list = draw_data->CmdLists[n]; @@ -302,26 +309,31 @@ CODE } else { - // The texture for the draw call is specified by pcmd->TextureId. - // The vast majority of draw calls will use the Dear ImGui texture atlas, which value you have set yourself during initialization. - MyEngineBindTexture((MyTexture*)pcmd->TextureId); + // Project scissor/clipping rectangles into framebuffer space + ImVec2 clip_min(pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y); + ImVec2 clip_max(pcmd->ClipRect.z - clip_off.x, pcmd->ClipRect.w - clip_off.y); + if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) + continue; - // We are using scissoring to clip some objects. All low-level graphics API should supports it. + // We are using scissoring to clip some objects. All low-level graphics API should support it. // - If your engine doesn't support scissoring yet, you may ignore this at first. You will get some small glitches // (some elements visible outside their bounds) but you can fix that once everything else works! - // - Clipping coordinates are provided in imgui coordinates space (from draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize) - // In a single viewport application, draw_data->DisplayPos will always be (0,0) and draw_data->DisplaySize will always be == io.DisplaySize. - // However, in the interest of supporting multi-viewport applications in the future (see 'viewport' branch on github), - // always subtract draw_data->DisplayPos from clipping bounds to convert them to your viewport space. + // - Clipping coordinates are provided in imgui coordinates space: + // - For a given viewport, draw_data->DisplayPos == viewport->Pos and draw_data->DisplaySize == viewport->Size + // - In a single viewport application, draw_data->DisplayPos == (0,0) and draw_data->DisplaySize == io.DisplaySize, but always use GetMainViewport()->Pos/Size instead of hardcoding those values. + // - In the interest of supporting multi-viewport applications (see 'docking' branch on github), + // always subtract draw_data->DisplayPos from clipping bounds to convert them to your viewport space. // - Note that pcmd->ClipRect contains Min+Max bounds. Some graphics API may use Min+Max, other may use Min+Size (size being Max-Min) - ImVec2 pos = draw_data->DisplayPos; - MyEngineScissor((int)(pcmd->ClipRect.x - pos.x), (int)(pcmd->ClipRect.y - pos.y), (int)(pcmd->ClipRect.z - pos.x), (int)(pcmd->ClipRect.w - pos.y)); + MyEngineSetScissor(clip_min.x, clip_min.y, clip_max.x, clip_max.y); + + // The texture for the draw call is specified by pcmd->GetTexID(). + // The vast majority of draw calls will use the Dear ImGui texture atlas, which value you have set yourself during initialization. + MyEngineBindTexture((MyTexture*)pcmd->GetTexID()); // Render 'pcmd->ElemCount/3' indexed triangles. // By default the indices ImDrawIdx are 16-bit, you can change them to 32-bit in imconfig.h if your engine doesn't support 16-bit indices. - MyEngineDrawIndexedTriangles(pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer, vtx_buffer); + MyEngineDrawIndexedTriangles(pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer + pcmd->IdxOffset, vtx_buffer, pcmd->VtxOffset); } - idx_buffer += pcmd->ElemCount; } } } @@ -334,33 +346,32 @@ CODE - You can ask questions and report issues at https://github.com/ocornut/imgui/issues/787 - The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable. - Keyboard: - - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable. - NewFrame() will automatically fill io.NavInputs[] based on your io.KeysDown[] + io.KeyMap[] arrays. - - When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard), the io.WantCaptureKeyboard flag - will be set. For more advanced uses, you may want to read from: + - Application: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable. + - Internally: NewFrame() will automatically fill io.NavInputs[] based on backend's io.AddKeyEvent() calls. + - When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard), + the io.WantCaptureKeyboard flag will be set. For more advanced uses, you may want to read from: - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set. - io.NavVisible: true when the navigation cursor is visible (and usually goes false when mouse is used). - or query focus information with e.g. IsWindowFocused(ImGuiFocusedFlags_AnyWindow), IsItemFocused() etc. functions. Please reach out if you think the game vs navigation input sharing could be improved. - Gamepad: - - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable. - - Backend: Set io.BackendFlags |= ImGuiBackendFlags_HasGamepad + fill the io.NavInputs[] fields before calling NewFrame(). - Note that io.NavInputs[] is cleared by EndFrame(). - - See 'enum ImGuiNavInput_' in imgui.h for a description of inputs. For each entry of io.NavInputs[], set the following values: - 0.0f= not held. 1.0f= fully held. Pass intermediate 0.0f..1.0f values for analog triggers/sticks. - - We uses a simple >0.0f test for activation testing, and won't attempt to test for a dead-zone. - Your code will probably need to transform your raw inputs (such as e.g. remapping your 0.2..0.9 raw input range to 0.0..1.0 imgui range, etc.). + - Application: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable. + - Backend: Set io.BackendFlags |= ImGuiBackendFlags_HasGamepad + call io.AddKeyEvent/AddKeyAnalogEvent() with ImGuiKey_Gamepad_XXX keys. + For analog values (0.0f to 1.0f), backend is responsible to handling a dead-zone and rescaling inputs accordingly. + Backend code will probably need to transform your raw inputs (such as e.g. remapping your 0.2..0.9 raw input range to 0.0..1.0 imgui range, etc.). + - Internally: NewFrame() will automatically fill io.NavInputs[] based on backend's io.AddKeyEvent() + io.AddKeyAnalogEvent() calls. + - BEFORE 1.87, BACKENDS USED TO WRITE DIRECTLY TO io.NavInputs[]. This is going to be obsoleted in the future. Please call io functions instead! - You can download PNG/PSD files depicting the gamepad controls for common controllers at: http://dearimgui.org/controls_sheets - - If you need to share inputs between your game and the imgui parts, the easiest approach is to go all-or-nothing, with a buttons combo - to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved. + - If you need to share inputs between your game and the Dear ImGui interface, the easiest approach is to go all-or-nothing, + with a buttons combo to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved. - Mouse: - - PS4 users: Consider emulating a mouse cursor with DualShock4 touch pad or a spare analog stick as a mouse-emulation fallback. + - PS4/PS5 users: Consider emulating a mouse cursor with DualShock4 touch pad or a spare analog stick as a mouse-emulation fallback. - Consoles/Tablet/Phone users: Consider using a Synergy 1.x server (on your PC) + uSynergy.c (on your console/tablet/phone app) to share your PC mouse/keyboard. - On a TV/console system where readability may be lower or mouse inputs may be awkward, you may want to set the ImGuiConfigFlags_NavEnableSetMousePos flag. Enabling ImGuiConfigFlags_NavEnableSetMousePos + ImGuiBackendFlags_HasSetMousePos instructs dear imgui to move your mouse cursor along with navigation movements. When enabled, the NewFrame() function may alter 'io.MousePos' and set 'io.WantSetMousePos' to notify you that it wants the mouse cursor to be moved. When that happens your backend NEEDS to move the OS or underlying mouse cursor on the next frame. Some of the backends in examples/ do that. - (If you set the NavEnableSetMousePos flag but don't honor 'io.WantSetMousePos' properly, imgui will misbehave as it will see your mouse as moving back and forth!) + (If you set the NavEnableSetMousePos flag but don't honor 'io.WantSetMousePos' properly, imgui will misbehave as it will see your mouse moving back and forth!) (In a setup when you may not have easy control over the mouse cursor, e.g. uSynergy.c doesn't expose moving remote mouse cursor, you may want to set a boolean to ignore your other external mouse positions until the external source is moved again.) @@ -370,9 +381,66 @@ CODE Occasionally introducing changes that are breaking the API. We try to make the breakage minor and easy to fix. Below is a change-log of API breaking changes only. If you are using one of the functions listed, expect to have to fix some code. - When you are not sure about a old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files. + When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files. You can read releases logs https://github.com/ocornut/imgui/releases for more details. + - 2022/01/20 (1.87) - inputs: reworded gamepad IO. + - Backend writing to io.NavInputs[] -> backend should call io.AddKeyEvent()/io.AddKeyAnalogEvent() with ImGuiKey_GamepadXXX values. + - 2022/01/19 (1.87) - sliders, drags: removed support for legacy arithmetic operators (+,+-,*,/) when inputing text. This doesn't break any api/code but a feature that used to be accessible by end-users (which seemingly no one used). + - 2022/01/17 (1.87) - inputs: reworked mouse IO. + - Backend writing to io.MousePos -> backend should call io.AddMousePosEvent() + - Backend writing to io.MouseDown[] -> backend should call io.AddMouseButtonEvent() + - Backend writing to io.MouseWheel -> backend should call io.AddMouseWheelEvent() + - Backend writing to io.MouseHoveredViewport -> backend should call io.AddMouseViewportEvent() [Docking branch w/ multi-viewports only] + - 2022/01/10 (1.87) - inputs: reworked keyboard IO. Removed io.KeyMap[], io.KeysDown[] in favor of calling io.AddKeyEvent(). Removed GetKeyIndex(), now unecessary. All IsKeyXXX() functions now take ImGuiKey values. All features are still functional until IMGUI_DISABLE_OBSOLETE_KEYIO is defined. Read Changelog and Release Notes for details. + - IsKeyPressed(MY_NATIVE_KEY_XXX) -> use IsKeyPressed(ImGuiKey_XXX) + - IsKeyPressed(GetKeyIndex(ImGuiKey_XXX)) -> use IsKeyPressed(ImGuiKey_XXX) + - Backend writing to io.KeyMap[],io.KeysDown[] -> backend should call io.AddKeyEvent() + - Backend writing to io.KeyCtrl, io.KeyShift.. -> backend should call io.AddKeyEvent() with ImGuiKey_ModXXX values. *IF YOU PULLED CODE BETWEEN 2021/01/10 and 2021/01/27: We used to have a io.AddKeyModsEvent() function which was now replaced by io.AddKeyEvent() with ImGuiKey_ModXXX values.* + - one case won't work with backward compatibility: if your custom backend used ImGuiKey as mock native indices (e.g. "io.KeyMap[ImGuiKey_A] = ImGuiKey_A") because those values are now larger than the legacy KeyDown[] array. Will assert. + - inputs: added ImGuiKey_ModCtrl/ImGuiKey_ModShift/ImGuiKey_ModAlt/ImGuiKey_ModSuper values to submit keyboard modifiers using io.AddKeyEvent(), instead of writing directly to io.KeyCtrl, io.KeyShift, io.KeyAlt, io.KeySuper. + - 2022/01/05 (1.87) - inputs: renamed ImGuiKey_KeyPadEnter to ImGuiKey_KeypadEnter to align with new symbols. Kept redirection enum. + - 2022/01/05 (1.87) - removed io.ImeSetInputScreenPosFn() in favor of more flexible io.SetPlatformImeDataFn(). Removed 'void* io.ImeWindowHandle' in favor of writing to 'void* ImGuiViewport::PlatformHandleRaw'. + - 2022/01/01 (1.87) - commented out redirecting functions/enums names that were marked obsolete in 1.69, 1.70, 1.71, 1.72 (March-July 2019) + - ImGui::SetNextTreeNodeOpen() -> use ImGui::SetNextItemOpen() + - ImGui::GetContentRegionAvailWidth() -> use ImGui::GetContentRegionAvail().x + - ImGui::TreeAdvanceToLabelPos() -> use ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetTreeNodeToLabelSpacing()); + - ImFontAtlas::CustomRect -> use ImFontAtlasCustomRect + - ImGuiColorEditFlags_RGB/HSV/HEX -> use ImGuiColorEditFlags_DisplayRGB/HSV/Hex + - 2021/12/20 (1.86) - backends: removed obsolete Marmalade backend (imgui_impl_marmalade.cpp) + example. Find last supported version at https://github.com/ocornut/imgui/wiki/Bindings + - 2021/11/04 (1.86) - removed CalcListClipping() function. Prefer using ImGuiListClipper which can return non-contiguous ranges. Please open an issue if you think you really need this function. + - 2021/08/23 (1.85) - removed GetWindowContentRegionWidth() function. keep inline redirection helper. can use 'GetWindowContentRegionMax().x - GetWindowContentRegionMin().x' instead for generally 'GetContentRegionAvail().x' is more useful. + - 2021/07/26 (1.84) - commented out redirecting functions/enums names that were marked obsolete in 1.67 and 1.69 (March 2019): + - ImGui::GetOverlayDrawList() -> use ImGui::GetForegroundDrawList() + - ImFont::GlyphRangesBuilder -> use ImFontGlyphRangesBuilder + - 2021/05/19 (1.83) - backends: obsoleted direct access to ImDrawCmd::TextureId in favor of calling ImDrawCmd::GetTexID(). + - if you are using official backends from the source tree: you have nothing to do. + - if you have copied old backend code or using your own: change access to draw_cmd->TextureId to draw_cmd->GetTexID(). + - 2021/03/12 (1.82) - upgraded ImDrawList::AddRect(), AddRectFilled(), PathRect() to use ImDrawFlags instead of ImDrawCornersFlags. + - ImDrawCornerFlags_TopLeft -> use ImDrawFlags_RoundCornersTopLeft + - ImDrawCornerFlags_BotRight -> use ImDrawFlags_RoundCornersBottomRight + - ImDrawCornerFlags_None -> use ImDrawFlags_RoundCornersNone etc. + flags now sanely defaults to 0 instead of 0x0F, consistent with all other flags in the API. + breaking: the default with rounding > 0.0f is now "round all corners" vs old implicit "round no corners": + - rounding == 0.0f + flags == 0 --> meant no rounding --> unchanged (common use) + - rounding > 0.0f + flags != 0 --> meant rounding --> unchanged (common use) + - rounding == 0.0f + flags != 0 --> meant no rounding --> unchanged (unlikely use) + - rounding > 0.0f + flags == 0 --> meant no rounding --> BREAKING (unlikely use): will now round all corners --> use ImDrawFlags_RoundCornersNone or rounding == 0.0f. + this ONLY matters for hard coded use of 0 + rounding > 0.0f. Use of named ImDrawFlags_RoundCornersNone (new) or ImDrawCornerFlags_None (old) are ok. + the old ImDrawCornersFlags used awkward default values of ~0 or 0xF (4 lower bits set) to signify "round all corners" and we sometimes encouraged using them as shortcuts. + legacy path still support use of hard coded ~0 or any value from 0x1 or 0xF. They will behave the same with legacy paths enabled (will assert otherwise). + - 2021/03/11 (1.82) - removed redirecting functions/enums names that were marked obsolete in 1.66 (September 2018): + - ImGui::SetScrollHere() -> use ImGui::SetScrollHereY() + - 2021/03/11 (1.82) - clarified that ImDrawList::PathArcTo(), ImDrawList::PathArcToFast() won't render with radius < 0.0f. Previously it sorts of accidentally worked but would generally lead to counter-clockwise paths and have an effect on anti-aliasing. + - 2021/03/10 (1.82) - upgraded ImDrawList::AddPolyline() and PathStroke() "bool closed" parameter to "ImDrawFlags flags". The matching ImDrawFlags_Closed value is guaranteed to always stay == 1 in the future. + - 2021/02/22 (1.82) - (*undone in 1.84*) win32+mingw: Re-enabled IME functions by default even under MinGW. In July 2016, issue #738 had me incorrectly disable those default functions for MinGW. MinGW users should: either link with -limm32, either set their imconfig file with '#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS'. + - 2021/02/17 (1.82) - renamed rarely used style.CircleSegmentMaxError (old default = 1.60f) to style.CircleTessellationMaxError (new default = 0.30f) as the meaning of the value changed. + - 2021/02/03 (1.81) - renamed ListBoxHeader(const char* label, ImVec2 size) to BeginListBox(). Kept inline redirection function (will obsolete). + - removed ListBoxHeader(const char* label, int items_count, int height_in_items = -1) in favor of specifying size. Kept inline redirection function (will obsolete). + - renamed ListBoxFooter() to EndListBox(). Kept inline redirection function (will obsolete). + - 2021/01/26 (1.81) - removed ImGuiFreeType::BuildFontAtlas(). Kept inline redirection function. Prefer using '#define IMGUI_ENABLE_FREETYPE', but there's a runtime selection path available too. The shared extra flags parameters (very rarely used) are now stored in ImFontAtlas::FontBuilderFlags. + - renamed ImFontConfig::RasterizerFlags (used by FreeType) to ImFontConfig::FontBuilderFlags. + - renamed ImGuiFreeType::XXX flags to ImGuiFreeTypeBuilderFlags_XXX for consistency with other API. - 2020/10/12 (1.80) - removed redirecting functions/enums that were marked obsolete in 1.63 (August 2018): - ImGui::IsItemDeactivatedAfterChange() -> use ImGui::IsItemDeactivatedAfterEdit(). - ImGuiCol_ModalWindowDarkening -> use ImGuiCol_ModalWindowDimBg @@ -396,7 +464,7 @@ CODE - 2020/09/25 (1.79) - renamed ImGuiSliderFlags_ClampOnInput to ImGuiSliderFlags_AlwaysClamp. Kept redirection enum (will obsolete sooner because previous name was added recently). - 2020/09/25 (1.79) - renamed style.TabMinWidthForUnselectedCloseButton to style.TabMinWidthForCloseButton. - 2020/09/21 (1.79) - renamed OpenPopupContextItem() back to OpenPopupOnItemClick(), reverting the change from 1.77. For varieties of reason this is more self-explanatory. - - 2020/09/21 (1.79) - removed return value from OpenPopupOnItemClick() - returned true on mouse release on item - because it is inconsistent with other popup APIs and makes others misleading. It's also and unnecessary: you can use IsWindowAppearing() after BeginPopup() for a similar result. + - 2020/09/21 (1.79) - removed return value from OpenPopupOnItemClick() - returned true on mouse release on an item - because it is inconsistent with other popup APIs and makes others misleading. It's also and unnecessary: you can use IsWindowAppearing() after BeginPopup() for a similar result. - 2020/09/17 (1.79) - removed ImFont::DisplayOffset in favor of ImFontConfig::GlyphOffset. DisplayOffset was applied after scaling and not very meaningful/useful outside of being needed by the default ProggyClean font. If you scaled this value after calling AddFontDefault(), this is now done automatically. It was also getting in the way of better font scaling, so let's get rid of it now! - 2020/08/17 (1.78) - obsoleted use of the trailing 'float power=1.0f' parameter for DragFloat(), DragFloat2(), DragFloat3(), DragFloat4(), DragFloatRange2(), DragScalar(), DragScalarN(), SliderFloat(), SliderFloat2(), SliderFloat3(), SliderFloat4(), SliderScalar(), SliderScalarN(), VSliderFloat() and VSliderScalar(). replaced the 'float power=1.0f' argument with integer-based flags defaulting to 0 (as with all our flags). @@ -405,7 +473,7 @@ CODE - if you set the 'power' parameter to 1.0f (same as previous default value): 1/ your compiler may warn on float>int conversion, 2/ everything else will work. 3/ you can replace the 1.0f value with 0 to fix the warning, and be technically correct. - if you set the 'power' parameter to >1.0f (to enable non-linear editing): 1/ your compiler may warn on float>int conversion, 2/ code will assert at runtime, 3/ in case asserts are disabled, the code will not crash and enable the _Logarithmic flag. 4/ you can replace the >1.0f value with ImGuiSliderFlags_Logarithmic to fix the warning/assert and get a _similar_ effect as previous uses of power >1.0f. see https://github.com/ocornut/imgui/issues/3361 for all details. - kept inline redirection functions (will obsolete) apart for: DragFloatRange2(), VSliderFloat(), VSliderScalar(). For those three the 'float power=1.0f' version were removed directly as they were most unlikely ever used. + kept inline redirection functions (will obsolete) apart for: DragFloatRange2(), VSliderFloat(), VSliderScalar(). For those three the 'float power=1.0f' version was removed directly as they were most unlikely ever used. for shared code, you can version check at compile-time with `#if IMGUI_VERSION_NUM >= 17704`. - obsoleted use of v_min > v_max in DragInt, DragFloat, DragScalar to lock edits (introduced in 1.73, was not demoed nor documented very), will be replaced by a more generic ReadOnly feature. You may use the ImGuiSliderFlags_ReadOnly internal flag in the meantime. - 2020/06/23 (1.77) - removed BeginPopupContextWindow(const char*, int mouse_button, bool also_over_items) in favor of BeginPopupContextWindow(const char*, ImGuiPopupFlags flags) with ImGuiPopupFlags_NoOverItems. @@ -425,7 +493,7 @@ CODE - ImGuiStyleVar_ChildWindowRounding -> use ImGuiStyleVar_ChildRounding - ImGuiTreeNodeFlags_AllowOverlapMode -> use ImGuiTreeNodeFlags_AllowItemOverlap - IMGUI_DISABLE_TEST_WINDOWS -> use IMGUI_DISABLE_DEMO_WINDOWS - - 2019/12/08 (1.75) - obsoleted calling ImDrawList::PrimReserve() with a negative count (which was the vaguely documented and rarely if ever used). Instead we added an explicit PrimUnreserve() API. + - 2019/12/08 (1.75) - obsoleted calling ImDrawList::PrimReserve() with a negative count (which was vaguely documented and rarely if ever used). Instead, we added an explicit PrimUnreserve() API. - 2019/12/06 (1.75) - removed implicit default parameter to IsMouseDragging(int button = 0) to be consistent with other mouse functions (none of the other functions have it). - 2019/11/21 (1.74) - ImFontAtlas::AddCustomRectRegular() now requires an ID larger than 0x110000 (instead of 0x10000) to conform with supporting Unicode planes 1-16 in a future update. ID below 0x110000 will now assert. - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS to IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS for consistency. @@ -453,7 +521,7 @@ CODE - 2019/04/29 (1.70) - removed GetContentRegionAvailWidth(), use GetContentRegionAvail().x instead. Kept inline redirection function (will obsolete). - 2019/03/04 (1.69) - renamed GetOverlayDrawList() to GetForegroundDrawList(). Kept redirection function (will obsolete). - 2019/02/26 (1.69) - renamed ImGuiColorEditFlags_RGB/ImGuiColorEditFlags_HSV/ImGuiColorEditFlags_HEX to ImGuiColorEditFlags_DisplayRGB/ImGuiColorEditFlags_DisplayHSV/ImGuiColorEditFlags_DisplayHex. Kept redirection enums (will obsolete). - - 2019/02/14 (1.68) - made it illegal/assert when io.DisplayTime == 0.0f (with an exception for the first frame). If for some reason your time step calculation gives you a zero value, replace it with an arbitrary small value! + - 2019/02/14 (1.68) - made it illegal/assert when io.DisplayTime == 0.0f (with an exception for the first frame). If for some reason your time step calculation gives you a zero value, replace it with an arbitrarily small value! - 2019/02/01 (1.68) - removed io.DisplayVisibleMin/DisplayVisibleMax (which were marked obsolete and removed from viewport/docking branch already). - 2019/01/06 (1.67) - renamed io.InputCharacters[], marked internal as was always intended. Please don't access directly, and use AddInputCharacter() instead! - 2019/01/06 (1.67) - renamed ImFontAtlas::GlyphRangesBuilder to ImFontGlyphRangesBuilder. Kept redirection typedef (will obsolete). @@ -548,12 +616,12 @@ CODE - 2017/07/20 (1.51) - removed IsPosHoveringAnyWindow(ImVec2), which was partly broken and misleading. ASSERT + redirect user to io.WantCaptureMouse - 2017/05/26 (1.50) - removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset. - 2017/05/01 (1.50) - renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity. - - 2016/11/06 (1.50) - BeginChild(const char*) now applies the stack id to the provided label, consistently with other functions as it should always have been. It shouldn't affect you unless (extremely unlikely) you were appending multiple times to a same child from different locations of the stack id. If that's the case, generate an id with GetId() and use it instead of passing string to BeginChild(). + - 2016/11/06 (1.50) - BeginChild(const char*) now applies the stack id to the provided label, consistently with other functions as it should always have been. It shouldn't affect you unless (extremely unlikely) you were appending multiple times to a same child from different locations of the stack id. If that's the case, generate an id with GetID() and use it instead of passing string to BeginChild(). - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it. - 2016/09/25 (1.50) - style.WindowTitleAlign is now a ImVec2 (ImGuiAlign enum was removed). set to (0.5f,0.5f) for horizontal+vertical centering, (0.0f,0.0f) for upper-left, etc. - - 2016/07/30 (1.50) - SameLine(x) with x>0.0f is now relative to left of column/group if any, and not always to left of window. This was sort of always the intent and hopefully breakage should be minimal. + - 2016/07/30 (1.50) - SameLine(x) with x>0.0f is now relative to left of column/group if any, and not always to left of window. This was sort of always the intent and hopefully, breakage should be minimal. - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore. - If your TitleBg/TitleBgActive alpha was 1.0f or you are using the default theme it will not affect you, otherwise if <1.0f you need tweak your custom theme to readjust for the fact that we don't draw a WindowBg background behind the title bar. + If your TitleBg/TitleBgActive alpha was 1.0f or you are using the default theme it will not affect you, otherwise if <1.0f you need to tweak your custom theme to readjust for the fact that we don't draw a WindowBg background behind the title bar. This helper function will convert an old TitleBg/TitleBgActive color into a new one with the same visual output, given the OLD color and the OLD WindowBg color: ImVec4 ConvertTitleBgCol(const ImVec4& win_bg_col, const ImVec4& title_bg_col) { float new_a = 1.0f - ((1.0f - win_bg_col.w) * (1.0f - title_bg_col.w)), k = title_bg_col.w / new_a; return ImVec4((win_bg_col.x * win_bg_col.w + title_bg_col.x) * k, (win_bg_col.y * win_bg_col.w + title_bg_col.y) * k, (win_bg_col.z * win_bg_col.w + title_bg_col.z) * k, new_a); } If this is confusing, pick the RGB value from title bar from an old screenshot and apply this as TitleBg/TitleBgActive. Or you may just create TitleBgActive from a tweaked TitleBg color. @@ -561,7 +629,7 @@ CODE - 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection. - 2016/05/01 (1.49) - obsoleted old signature of CollapsingHeader(const char* label, const char* str_id = NULL, bool display_frame = true, bool default_open = false) as extra parameters were badly designed and rarely used. You can replace the "default_open = true" flag in new API with CollapsingHeader(label, ImGuiTreeNodeFlags_DefaultOpen). - 2016/04/26 (1.49) - changed ImDrawList::PushClipRect(ImVec4 rect) to ImDrawList::PushClipRect(Imvec2 min,ImVec2 max,bool intersect_with_current_clip_rect=false). Note that higher-level ImGui::PushClipRect() is preferable because it will clip at logic/widget level, whereas ImDrawList::PushClipRect() only affect your renderer. - - 2016/04/03 (1.48) - removed style.WindowFillAlphaDefault setting which was redundant. Bake default BG alpha inside style.Colors[ImGuiCol_WindowBg] and all other Bg color values. (ref github issue #337). + - 2016/04/03 (1.48) - removed style.WindowFillAlphaDefault setting which was redundant. Bake default BG alpha inside style.Colors[ImGuiCol_WindowBg] and all other Bg color values. (ref GitHub issue #337). - 2016/04/03 (1.48) - renamed ImGuiCol_TooltipBg to ImGuiCol_PopupBg, used by popups/menus and tooltips. popups/menus were previously using ImGuiCol_WindowBg. (ref github issue #337) - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete). - 2016/03/02 (1.48) - InputText() completion/history/always callbacks: if you modify the text buffer manually (without using DeleteChars()/InsertChars() helper) you need to maintain the BufTextLen field. added an assert. @@ -592,7 +660,7 @@ CODE - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete). - 2015/07/02 (1.42) - renamed GetScrollPosY() to GetScrollY(). Necessary to reduce confusion along with other scrolling functions, because positions (e.g. cursor position) are not equivalent to scrolling amount. - 2015/06/14 (1.41) - changed ImageButton() default bg_col parameter from (0,0,0,1) (black) to (0,0,0,0) (transparent) - makes a difference when texture have transparence - - 2015/06/14 (1.41) - changed Selectable() API from (label, selected, size) to (label, selected, flags, size). Size override should have been rarely be used. Sorry! + - 2015/06/14 (1.41) - changed Selectable() API from (label, selected, size) to (label, selected, flags, size). Size override should have been rarely used. Sorry! - 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete). - 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete). - 2015/05/27 (1.40) - removed the third 'repeat_if_held' parameter from Button() - sorry! it was rarely used and inconsistent. Use PushButtonRepeat(true) / PopButtonRepeat() to enable repeat on desired buttons. @@ -616,9 +684,9 @@ CODE - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader. - 2015/01/11 (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels. - old: const void* png_data; unsigned int png_size; ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); [..Upload texture to GPU..]; - - new: unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); [..Upload texture to GPU..]; io.Fonts->TexId = YourTexIdentifier; + - new: unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); [..Upload texture to GPU..]; io.Fonts->SetTexID(YourTexIdentifier); you now have more flexibility to load multiple TTF fonts and manage the texture buffer for internal needs. It is now recommended that you sample the font texture with bilinear interpolation. - - 2015/01/11 (1.30) - added texture identifier in ImDrawCmd passed to your render function (we can now render images). make sure to set io.Fonts->TexID. + - 2015/01/11 (1.30) - added texture identifier in ImDrawCmd passed to your render function (we can now render images). make sure to call io.Fonts->SetTexID() - 2015/01/11 (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix) - 2015/01/11 (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets - 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver) @@ -647,7 +715,7 @@ CODE =========== Q: Where is the documentation? - A: This library is poorly documented at the moment and expects of the user to be acquainted with C/C++. + A: This library is poorly documented at the moment and expects the user to be acquainted with C/C++. - Run the examples/ and explore them. - See demo code in imgui_demo.cpp and particularly the ImGui::ShowDemoWindow() function. - The demo covers most features of Dear ImGui, so you can read the code and see its output. @@ -657,7 +725,7 @@ CODE - The Wiki (https://github.com/ocornut/imgui/wiki) has many resources and links. - The Glossary (https://github.com/ocornut/imgui/wiki/Glossary) page also may be useful. - Your programming IDE is your friend, find the type or function declaration to find comments - associated to it. + associated with it. Q: What is this library called? Q: Which version should I get? @@ -670,23 +738,25 @@ CODE Q: How to get started? A: Read 'PROGRAMMER GUIDE' above. Read examples/README.txt. - Q: How can I tell whether to dispatch mouse/keyboard to Dear ImGui or to my application? + Q: How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application? A: You should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags! - >> See https://www.dearimgui.org/faq for fully detailed answer. You really want to read this. + >> See https://www.dearimgui.org/faq for a fully detailed answer. You really want to read this. Q. How can I enable keyboard controls? Q: How can I use this without a mouse, without a keyboard or without a screen? (gamepad, input share, remote display) - Q: I integrated Dear ImGui in my engine and little squares are showing instead of text.. - Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around.. - Q: I integrated Dear ImGui in my engine and some elements are displaying outside their expected windows boundaries.. + Q: I integrated Dear ImGui in my engine and little squares are showing instead of text... + Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around... + Q: I integrated Dear ImGui in my engine and some elements are displaying outside their expected windows boundaries... >> See https://www.dearimgui.org/faq Q&A: Usage ---------- - Q: Why is my widget not reacting when I click on it? - Q: How can I have widgets with an empty label? - Q: How can I have multiple widgets with the same label? + Q: About the ID Stack system.. + - Why is my widget not reacting when I click on it? + - How can I have widgets with an empty label? + - How can I have multiple widgets with the same label? + - How can I have multiple windows with the same label? Q: How can I display an image? What is ImTextureID, how does it works? Q: How can I use my own math types instead of ImVec2/ImVec4? Q: How can I interact with standard C++ types (such as std::string and std::vector)? @@ -716,16 +786,16 @@ CODE ============== Q: How can I help? - A: - Businesses: please reach out to "contact AT dearimgui.org" if you work in a place using Dear ImGui! + A: - Businesses: please reach out to "contact AT dearimgui.com" if you work in a place using Dear ImGui! We can discuss ways for your company to fund development via invoiced technical support, maintenance or sponsoring contacts. - This is among the most useful thing you can do for Dear ImGui. With increased funding we can hire more people working on this project. + This is among the most useful thing you can do for Dear ImGui. With increased funding, we can hire more people working on this project. - Individuals: you can support continued development via PayPal donations. See README. - - If you are experienced with Dear ImGui and C++, look at the github issues, look at the Wiki, read docs/TODO.txt + - If you are experienced with Dear ImGui and C++, look at the GitHub issues, look at the Wiki, read docs/TODO.txt and see how you want to help and can help! - Disclose your usage of Dear ImGui via a dev blog post, a tweet, a screenshot, a mention somewhere etc. - You may post screenshot or links in the gallery threads (github.com/ocornut/imgui/issues/3488). Visuals are ideal as they inspire other programmers. - But even without visuals, disclosing your use of dear imgui help the library grow credibility, and help other teams and programmers with taking decisions. - - If you have issues or if you need to hack into the library, even if you don't expect any support it is useful that you share your issues (on github or privately). + You may post screenshot or links in the gallery threads. Visuals are ideal as they inspire other programmers. + But even without visuals, disclosing your use of dear imgui helps the library grow credibility, and help other teams and programmers with taking decisions. + - If you have issues or if you need to hack into the library, even if you don't expect any support it is useful that you share your issues (on GitHub or privately). */ @@ -754,6 +824,11 @@ CODE #include // intptr_t #endif +// [Windows] On non-Visual Studio compilers, we default to IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS unless explicitly enabled +#if defined(_WIN32) && !defined(_MSC_VER) && !defined(IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS) +#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS +#endif + // [Windows] OS specific includes (optional) #if defined(_WIN32) && defined(IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS) && defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) && defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) #define IMGUI_DISABLE_WIN32_FUNCTIONS @@ -788,6 +863,9 @@ CODE #if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later #pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types #endif +#pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2). +#pragma warning (disable: 26495) // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6). +#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). #endif // Clang/GCC warnings with -Weverything @@ -830,7 +908,7 @@ static const float NAV_WINDOWING_HIGHLIGHT_DELAY = 0.20f; // Time static const float NAV_WINDOWING_LIST_APPEAR_DELAY = 0.15f; // Time before the window list starts to appear // Window resizing from edges (when io.ConfigWindowsResizeFromEdges = true and ImGuiBackendFlags_HasMouseCursors is set in io.BackendFlags by backend) -static const float WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS = 4.0f; // Extend outside and inside windows. Affect FindHoveredWindow(). +static const float WINDOWS_HOVER_PADDING = 4.0f; // Extend outside window for hovering/resizing (maxxed with TouchPadding) and inside windows for borders. Affect FindHoveredWindow(). static const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f; // Reduce visual noise by only highlighting the border after a certain time. static const float WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER = 2.00f; // Lock scrolled window (so it doesn't pick child windows that are scrolling through) for a certain time, unless mouse moved. @@ -846,8 +924,6 @@ static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* wind static void AddDrawListToDrawData(ImVector* out_list, ImDrawList* draw_list); static void AddWindowToSortBuffer(ImVector* out_sorted_windows, ImGuiWindow* window); -static ImRect GetViewportRect(); - // Settings static void WindowSettingsHandler_ClearAll(ImGuiContext*, ImGuiSettingsHandler*); static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name); @@ -858,7 +934,7 @@ static void WindowSettingsHandler_WriteAll(ImGuiContext*, ImGuiSetti // Platform Dependents default implementation for IO functions static const char* GetClipboardTextFn_DefaultImpl(void* user_data); static void SetClipboardTextFn_DefaultImpl(void* user_data, const char* text); -static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y); +static void SetPlatformImeDataFn_DefaultImpl(ImGuiViewport* viewport, ImGuiPlatformImeData* data); namespace ImGui { @@ -866,33 +942,45 @@ namespace ImGui static void NavUpdate(); static void NavUpdateWindowing(); static void NavUpdateWindowingOverlay(); -static void NavUpdateMoveResult(); -static void NavUpdateInitResult(); +static void NavUpdateCancelRequest(); +static void NavUpdateCreateMoveRequest(); +static void NavUpdateCreateTabbingRequest(); static float NavUpdatePageUpPageDown(); static inline void NavUpdateAnyRequestFlag(); +static void NavUpdateCreateWrappingRequest(); static void NavEndFrame(); -static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand); -static void NavApplyItemToResult(ImGuiNavMoveResult* result, ImGuiWindow* window, ImGuiID id, const ImRect& nav_bb_rel); -static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, ImGuiID id); +static bool NavScoreItem(ImGuiNavItemData* result); +static void NavApplyItemToResult(ImGuiNavItemData* result); +static void NavProcessItem(); +static void NavProcessItemForTabbingRequest(ImGuiID id); static ImVec2 NavCalcPreferredRefPos(); static void NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window); static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window); +static void NavRestoreLayer(ImGuiNavLayer layer); +static void NavRestoreHighlightAfterMove(); static int FindWindowFocusIndex(ImGuiWindow* window); -// Error Checking +// Error Checking and Debug Tools static void ErrorCheckNewFrameSanityChecks(); static void ErrorCheckEndFrameSanityChecks(); +static void UpdateDebugToolItemPicker(); +static void UpdateDebugToolStackQueries(); // Misc static void UpdateSettings(); +static void UpdateKeyboardInputs(); static void UpdateMouseInputs(); static void UpdateMouseWheel(); -static void UpdateTabFocus(); -static void UpdateDebugToolItemPicker(); static bool UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect); static void RenderWindowOuterBorders(ImGuiWindow* window); static void RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size); static void RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open); +static void RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col); +static void RenderDimmedBackgrounds(); +static ImGuiWindow* FindBlockingModal(ImGuiWindow* window); + +// Viewports +static void UpdateViewportsNewFrame(); } @@ -900,27 +988,33 @@ static void RenderWindowTitleBarContents(ImGuiWindow* window, const // [SECTION] CONTEXT AND MEMORY ALLOCATORS //----------------------------------------------------------------------------- +// DLL users: +// - Heaps and globals are not shared across DLL boundaries! +// - You will need to call SetCurrentContext() + SetAllocatorFunctions() for each static/DLL boundary you are calling from. +// - Same applies for hot-reloading mechanisms that are reliant on reloading DLL (note that many hot-reloading mechanisms work without DLL). +// - Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility. +// - Confused? In a debugger: add GImGui to your watch window and notice how its value changes depending on your current location (which DLL boundary you are in). + // Current context pointer. Implicitly used by all Dear ImGui functions. Always assumed to be != NULL. -// ImGui::CreateContext() will automatically set this pointer if it is NULL. Change to a different context by calling ImGui::SetCurrentContext(). -// 1) Important: globals are not shared across DLL boundaries! If you use DLLs or any form of hot-reloading: you will need to call -// SetCurrentContext() (with the pointer you got from CreateContext) from each unique static/DLL boundary, and after each hot-reloading. -// In your debugger, add GImGui to your watch window and notice how its value changes depending on which location you are currently stepping into. -// 2) Important: Dear ImGui functions are not thread-safe because of this pointer. -// If you want thread-safety to allow N threads to access N different contexts, you can: -// - Change this variable to use thread local storage so each thread can refer to a different context, in imconfig.h: -// struct ImGuiContext; -// extern thread_local ImGuiContext* MyImGuiTLS; -// #define GImGui MyImGuiTLS -// And then define MyImGuiTLS in one of your cpp file. Note that thread_local is a C++11 keyword, earlier C++ uses compiler-specific keyword. -// - Future development aim to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586 -// - If you need a finite number of contexts, you may compile and use multiple instances of the ImGui code from different namespace. +// - ImGui::CreateContext() will automatically set this pointer if it is NULL. +// Change to a different context by calling ImGui::SetCurrentContext(). +// - Important: Dear ImGui functions are not thread-safe because of this pointer. +// If you want thread-safety to allow N threads to access N different contexts: +// - Change this variable to use thread local storage so each thread can refer to a different context, in your imconfig.h: +// struct ImGuiContext; +// extern thread_local ImGuiContext* MyImGuiTLS; +// #define GImGui MyImGuiTLS +// And then define MyImGuiTLS in one of your cpp files. Note that thread_local is a C++11 keyword, earlier C++ uses compiler-specific keyword. +// - Future development aims to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586 +// - If you need a finite number of contexts, you may compile and use multiple instances of the ImGui code from a different namespace. +// - DLL users: read comments above. #ifndef GImGui ImGuiContext* GImGui = NULL; #endif // Memory Allocator functions. Use SetAllocatorFunctions() to change them. -// If you use DLL hotreloading you might need to call SetAllocatorFunctions() after reloading code from this file. -// Otherwise, you probably don't want to modify them mid-program, and if you use global/static e.g. ImVector<> instances you may need to keep them accessible during program destruction. +// - You probably don't want to modify that mid-program, and if you use global/static e.g. ImVector<> instances you may need to keep them accessible during program destruction. +// - DLL users: read comments above. #ifndef IMGUI_DISABLE_DEFAULT_ALLOCATORS static void* MallocWrapper(size_t size, void* user_data) { IM_UNUSED(user_data); return malloc(size); } static void FreeWrapper(void* ptr, void* user_data) { IM_UNUSED(user_data); free(ptr); } @@ -928,10 +1022,9 @@ static void FreeWrapper(void* ptr, void* user_data) { IM_UNUSED(user_d static void* MallocWrapper(size_t size, void* user_data) { IM_UNUSED(user_data); IM_UNUSED(size); IM_ASSERT(0); return NULL; } static void FreeWrapper(void* ptr, void* user_data) { IM_UNUSED(user_data); IM_UNUSED(ptr); IM_ASSERT(0); } #endif - -static void* (*GImAllocatorAllocFunc)(size_t size, void* user_data) = MallocWrapper; -static void (*GImAllocatorFreeFunc)(void* ptr, void* user_data) = FreeWrapper; -static void* GImAllocatorUserData = NULL; +static ImGuiMemAllocFunc GImAllocatorAllocFunc = MallocWrapper; +static ImGuiMemFreeFunc GImAllocatorFreeFunc = FreeWrapper; +static void* GImAllocatorUserData = NULL; //----------------------------------------------------------------------------- // [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO) @@ -939,9 +1032,10 @@ static void* GImAllocatorUserData = NULL; ImGuiStyle::ImGuiStyle() { - Alpha = 1.0f; // Global alpha applies to everything in ImGui + Alpha = 1.0f; // Global alpha applies to everything in Dear ImGui. + DisabledAlpha = 0.60f; // Additional alpha multiplier applied by BeginDisabled(). Multiply over current value of Alpha. WindowPadding = ImVec2(8,8); // Padding within a window - WindowRounding = 9.0f; // Radius of window corners rounding. Set to 0.0f to have rectangular windows. Large values tend to lead to variety of artifacts and are not recommended. + WindowRounding = 0.0f; // Radius of window corners rounding. Set to 0.0f to have rectangular windows. Large values tend to lead to variety of artifacts and are not recommended. WindowBorderSize = 1.0f; // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested. WindowMinSize = ImVec2(32,32); // Minimum window size WindowTitleAlign = ImVec2(0.0f,0.5f);// Alignment for title bar text @@ -977,10 +1071,10 @@ ImGuiStyle::ImGuiStyle() AntiAliasedLinesUseTex = true; // Enable anti-aliased lines/borders using textures where possible. Require backend to render with bilinear filtering. AntiAliasedFill = true; // Enable anti-aliased filled shapes (rounded rectangles, circles, etc.). CurveTessellationTol = 1.25f; // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality. - CircleSegmentMaxError = 1.60f; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry. + CircleTessellationMaxError = 0.30f; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry. // Default theme - ImGui::StyleColorsClassic(this); + ImGui::StyleColorsDark(this); } // To scale your entire UI (e.g. if you want your app to use High DPI or generally be DPI aware) you may use this helper function. Scaling the fonts is done separately and is up to you. @@ -1016,7 +1110,7 @@ ImGuiIO::ImGuiIO() { // Most fields are initialized with zero memset(this, 0, sizeof(*this)); - IM_ASSERT(IM_ARRAYSIZE(ImGuiIO::MouseDown) == ImGuiMouseButton_COUNT && IM_ARRAYSIZE(ImGuiIO::MouseClicked) == ImGuiMouseButton_COUNT); // Our pre-C++11 IM_STATIC_ASSERT() macros triggers warning on modern compilers so we don't use it here. + IM_STATIC_ASSERT(IM_ARRAYSIZE(ImGuiIO::MouseDown) == ImGuiMouseButton_COUNT && IM_ARRAYSIZE(ImGuiIO::MouseClicked) == ImGuiMouseButton_COUNT); // Settings ConfigFlags = ImGuiConfigFlags_None; @@ -1024,12 +1118,14 @@ ImGuiIO::ImGuiIO() DisplaySize = ImVec2(-1.0f, -1.0f); DeltaTime = 1.0f / 60.0f; IniSavingRate = 5.0f; - IniFilename = "imgui.ini"; + IniFilename = "imgui.ini"; // Important: "imgui.ini" is relative to current working dir, most apps will want to lock this to an absolute path (e.g. same path as executables). LogFilename = "imgui_log.txt"; MouseDoubleClickTime = 0.30f; MouseDoubleClickMaxDist = 6.0f; +#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO for (int i = 0; i < ImGuiKey_COUNT; i++) KeyMap[i] = -1; +#endif KeyRepeatDelay = 0.275f; KeyRepeatRate = 0.050f; UserData = NULL; @@ -1047,6 +1143,7 @@ ImGuiIO::ImGuiIO() #else ConfigMacOSXBehaviors = false; #endif + ConfigInputTrickleEventQueue = true; ConfigInputTextCursorBlink = true; ConfigWindowsResizeFromEdges = true; ConfigWindowsMoveFromTitleBarOnly = false; @@ -1058,25 +1155,35 @@ ImGuiIO::ImGuiIO() GetClipboardTextFn = GetClipboardTextFn_DefaultImpl; // Platform dependent default implementations SetClipboardTextFn = SetClipboardTextFn_DefaultImpl; ClipboardUserData = NULL; - ImeSetInputScreenPosFn = ImeSetInputScreenPosFn_DefaultImpl; - ImeWindowHandle = NULL; + SetPlatformImeDataFn = SetPlatformImeDataFn_DefaultImpl; // Input (NB: we already have memset zero the entire structure!) MousePos = ImVec2(-FLT_MAX, -FLT_MAX); MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX); MouseDragThreshold = 6.0f; for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f; - for (int i = 0; i < IM_ARRAYSIZE(KeysDownDuration); i++) KeysDownDuration[i] = KeysDownDurationPrev[i] = -1.0f; + for (int i = 0; i < IM_ARRAYSIZE(KeysData); i++) { KeysData[i].DownDuration = KeysData[i].DownDurationPrev = -1.0f; } for (int i = 0; i < IM_ARRAYSIZE(NavInputsDownDuration); i++) NavInputsDownDuration[i] = -1.0f; + BackendUsingLegacyKeyArrays = (ImS8)-1; + BackendUsingLegacyNavInputArray = true; // assume using legacy array until proven wrong } // Pass in translated ASCII characters for text input. // - with glfw you can get those from the callback set in glfwSetCharCallback() // - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message +// FIXME: Should in theory be called "AddCharacterEvent()" to be consistent with new API void ImGuiIO::AddInputCharacter(unsigned int c) { - if (c != 0) - InputQueueCharacters.push_back(c <= IM_UNICODE_CODEPOINT_MAX ? (ImWchar)c : IM_UNICODE_CODEPOINT_INVALID); + ImGuiContext& g = *GImGui; + IM_ASSERT(&g.IO == this && "Can only add events to current context."); + if (c == 0) + return; + + ImGuiInputEvent e; + e.Type = ImGuiInputEventType_Char; + e.Source = ImGuiInputSource_Keyboard; + e.Text.Char = c; + g.InputEventsQueue.push_back(e); } // UTF16 strings use surrogate pairs to encode codepoints >= 0x10000, so @@ -1089,7 +1196,7 @@ void ImGuiIO::AddInputCharacterUTF16(ImWchar16 c) if ((c & 0xFC00) == 0xD800) // High surrogate, must save { if (InputQueueSurrogate != 0) - InputQueueCharacters.push_back(IM_UNICODE_CODEPOINT_INVALID); + AddInputCharacter(IM_UNICODE_CODEPOINT_INVALID); InputQueueSurrogate = c; return; } @@ -1098,14 +1205,21 @@ void ImGuiIO::AddInputCharacterUTF16(ImWchar16 c) if (InputQueueSurrogate != 0) { if ((c & 0xFC00) != 0xDC00) // Invalid low surrogate - InputQueueCharacters.push_back(IM_UNICODE_CODEPOINT_INVALID); - else if (IM_UNICODE_CODEPOINT_MAX == (0xFFFF)) // Codepoint will not fit in ImWchar (extra parenthesis around 0xFFFF somehow fixes -Wunreachable-code with Clang) - cp = IM_UNICODE_CODEPOINT_INVALID; + { + AddInputCharacter(IM_UNICODE_CODEPOINT_INVALID); + } else + { +#if IM_UNICODE_CODEPOINT_MAX == 0xFFFF + cp = IM_UNICODE_CODEPOINT_INVALID; // Codepoint will not fit in ImWchar +#else cp = (ImWchar)(((InputQueueSurrogate - 0xD800) << 10) + (c - 0xDC00) + 0x10000); +#endif + } + InputQueueSurrogate = 0; } - InputQueueCharacters.push_back(cp); + AddInputCharacter((unsigned)cp); } void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars) @@ -1115,7 +1229,7 @@ void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars) unsigned int c = 0; utf8_chars += ImTextCharFromUtf8(&c, utf8_chars, NULL); if (c != 0) - InputQueueCharacters.push_back((ImWchar)c); + AddInputCharacter(c); } } @@ -1124,13 +1238,159 @@ void ImGuiIO::ClearInputCharacters() InputQueueCharacters.resize(0); } +void ImGuiIO::ClearInputKeys() +{ +#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO + memset(KeysDown, 0, sizeof(KeysDown)); +#endif + for (int n = 0; n < IM_ARRAYSIZE(KeysData); n++) + { + KeysData[n].Down = false; + KeysData[n].DownDuration = -1.0f; + KeysData[n].DownDurationPrev = -1.0f; + } + KeyCtrl = KeyShift = KeyAlt = KeySuper = false; + KeyMods = KeyModsPrev = ImGuiKeyModFlags_None; + for (int n = 0; n < IM_ARRAYSIZE(NavInputsDownDuration); n++) + NavInputsDownDuration[n] = NavInputsDownDurationPrev[n] = -1.0f; +} + +// Queue a new key down/up event. +// - ImGuiKey key: Translated key (as in, generally ImGuiKey_A matches the key end-user would use to emit an 'A' character) +// - bool down: Is the key down? use false to signify a key release. +// - float analog_value: 0.0f..1.0f +void ImGuiIO::AddKeyAnalogEvent(ImGuiKey key, bool down, float analog_value) +{ + //if (e->Down) { IMGUI_DEBUG_LOG("AddKeyEvent() Key='%s' %d, NativeKeycode = %d, NativeScancode = %d\n", ImGui::GetKeyName(e->Key), e->Down, e->NativeKeycode, e->NativeScancode); } + if (key == ImGuiKey_None) + return; + ImGuiContext& g = *GImGui; + IM_ASSERT(&g.IO == this && "Can only add events to current context."); + IM_ASSERT(ImGui::IsNamedKey(key)); // Backend needs to pass a valid ImGuiKey_ constant. 0..511 values are legacy native key codes which are not accepted by this API. + + // Verify that backend isn't mixing up using new io.AddKeyEvent() api and old io.KeysDown[] + io.KeyMap[] data. +#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO + IM_ASSERT((BackendUsingLegacyKeyArrays == -1 || BackendUsingLegacyKeyArrays == 0) && "Backend needs to either only use io.AddKeyEvent(), either only fill legacy io.KeysDown[] + io.KeyMap[]. Not both!"); + if (BackendUsingLegacyKeyArrays == -1) + for (int n = ImGuiKey_NamedKey_BEGIN; n < ImGuiKey_NamedKey_END; n++) + IM_ASSERT(KeyMap[n] == -1 && "Backend needs to either only use io.AddKeyEvent(), either only fill legacy io.KeysDown[] + io.KeyMap[]. Not both!"); + BackendUsingLegacyKeyArrays = 0; +#endif + if (ImGui::IsGamepadKey(key)) + BackendUsingLegacyNavInputArray = false; + + // Partial filter of duplicates (not strictly needed, but makes data neater in particular for key mods and gamepad values which are most commonly spmamed) + ImGuiKeyData* key_data = ImGui::GetKeyData(key); + if (key_data->Down == down && key_data->AnalogValue == analog_value) + { + bool found = false; + for (int n = g.InputEventsQueue.Size - 1; n >= 0 && !found; n--) + if (g.InputEventsQueue[n].Type == ImGuiInputEventType_Key && g.InputEventsQueue[n].Key.Key == key) + found = true; + if (!found) + return; + } + + // Add event + ImGuiInputEvent e; + e.Type = ImGuiInputEventType_Key; + e.Source = ImGui::IsGamepadKey(key) ? ImGuiInputSource_Gamepad : ImGuiInputSource_Keyboard; + e.Key.Key = key; + e.Key.Down = down; + e.Key.AnalogValue = analog_value; + g.InputEventsQueue.push_back(e); +} + +void ImGuiIO::AddKeyEvent(ImGuiKey key, bool down) +{ + AddKeyAnalogEvent(key, down, down ? 1.0f : 0.0f); +} + +// [Optional] Call after AddKeyEvent(). +// Specify native keycode, scancode + Specify index for legacy <1.87 IsKeyXXX() functions with native indices. +// If you are writing a backend in 2022 or don't use IsKeyXXX() with native values that are not ImGuiKey values, you can avoid calling this. +void ImGuiIO::SetKeyEventNativeData(ImGuiKey key, int native_keycode, int native_scancode, int native_legacy_index) +{ + if (key == ImGuiKey_None) + return; + IM_ASSERT(ImGui::IsNamedKey(key)); // >= 512 + IM_ASSERT(native_legacy_index == -1 || ImGui::IsLegacyKey(native_legacy_index)); // >= 0 && <= 511 + IM_UNUSED(native_keycode); // Yet unused + IM_UNUSED(native_scancode); // Yet unused + + // Build native->imgui map so old user code can still call key functions with native 0..511 values. +#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO + const int legacy_key = (native_legacy_index != -1) ? native_legacy_index : native_keycode; + if (ImGui::IsLegacyKey(legacy_key)) + KeyMap[legacy_key] = key; +#else + IM_UNUSED(key); + IM_UNUSED(native_legacy_index); +#endif +} + +// Queue a mouse move event +void ImGuiIO::AddMousePosEvent(float x, float y) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(&g.IO == this && "Can only add events to current context."); + + ImGuiInputEvent e; + e.Type = ImGuiInputEventType_MousePos; + e.Source = ImGuiInputSource_Mouse; + e.MousePos.PosX = x; + e.MousePos.PosY = y; + g.InputEventsQueue.push_back(e); +} + +void ImGuiIO::AddMouseButtonEvent(int mouse_button, bool down) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(&g.IO == this && "Can only add events to current context."); + IM_ASSERT(mouse_button >= 0 && mouse_button < ImGuiMouseButton_COUNT); + + ImGuiInputEvent e; + e.Type = ImGuiInputEventType_MouseButton; + e.Source = ImGuiInputSource_Mouse; + e.MouseButton.Button = mouse_button; + e.MouseButton.Down = down; + g.InputEventsQueue.push_back(e); +} + +// Queue a mouse wheel event (most mouse/API will only have a Y component) +void ImGuiIO::AddMouseWheelEvent(float wheel_x, float wheel_y) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(&g.IO == this && "Can only add events to current context."); + if (wheel_x == 0.0f && wheel_y == 0.0f) + return; + + ImGuiInputEvent e; + e.Type = ImGuiInputEventType_MouseWheel; + e.Source = ImGuiInputSource_Mouse; + e.MouseWheel.WheelX = wheel_x; + e.MouseWheel.WheelY = wheel_y; + g.InputEventsQueue.push_back(e); +} + +void ImGuiIO::AddFocusEvent(bool focused) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(&g.IO == this && "Can only add events to current context."); + + ImGuiInputEvent e; + e.Type = ImGuiInputEventType_Focus; + e.AppFocused.Focused = focused; + g.InputEventsQueue.push_back(e); +} + //----------------------------------------------------------------------------- // [SECTION] MISC HELPERS/UTILITIES (Geometry functions) //----------------------------------------------------------------------------- ImVec2 ImBezierCubicClosestPoint(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, int num_segments) { - IM_ASSERT(num_segments > 0); // Use ImBezierClosestPointCasteljau() + IM_ASSERT(num_segments > 0); // Use ImBezierCubicClosestPointCasteljau() ImVec2 p_last = p1; ImVec2 p_closest; float p_closest_dist2 = FLT_MAX; @@ -1653,7 +1913,7 @@ int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end) } // Based on stb_to_utf8() from github.com/nothings/stb/ -static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c) +static inline int ImTextCharToUtf8_inline(char* buf, int buf_size, unsigned int c) { if (c < 0x80) { @@ -1688,6 +1948,13 @@ static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c) return 0; } +const char* ImTextCharToUtf8(char out_buf[5], unsigned int c) +{ + int count = ImTextCharToUtf8_inline(out_buf, 5, c); + out_buf[count] = 0; + return out_buf; +} + // Not optimal but we very rarely use this function. int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end) { @@ -1704,20 +1971,20 @@ static inline int ImTextCountUtf8BytesFromChar(unsigned int c) return 3; } -int ImTextStrToUtf8(char* buf, int buf_size, const ImWchar* in_text, const ImWchar* in_text_end) +int ImTextStrToUtf8(char* out_buf, int out_buf_size, const ImWchar* in_text, const ImWchar* in_text_end) { - char* buf_out = buf; - const char* buf_end = buf + buf_size; - while (buf_out < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text) + char* buf_p = out_buf; + const char* buf_end = out_buf + out_buf_size; + while (buf_p < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text) { unsigned int c = (unsigned int)(*in_text++); if (c < 0x80) - *buf_out++ = (char)c; + *buf_p++ = (char)c; else - buf_out += ImTextCharToUtf8(buf_out, (int)(buf_end - buf_out - 1), c); + buf_p += ImTextCharToUtf8_inline(buf_p, (int)(buf_end - buf_p - 1), c); } - *buf_out = 0; - return (int)(buf_out - buf); + *buf_p = 0; + return (int)(buf_p - out_buf); } int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end) @@ -1852,7 +2119,7 @@ void ImGuiStorage::BuildSortByKey() { struct StaticFunc { - static int IMGUI_CDECL PairCompareByID(const void* lhs, const void* rhs) + static int IMGUI_CDECL PairComparerByID(const void* lhs, const void* rhs) { // We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that. if (((const ImGuiStoragePair*)lhs)->key > ((const ImGuiStoragePair*)rhs)->key) return +1; @@ -1860,8 +2127,7 @@ void ImGuiStorage::BuildSortByKey() return 0; } }; - if (Data.Size > 1) - ImQsort(Data.Data, (size_t)Data.Size, sizeof(ImGuiStoragePair), StaticFunc::PairCompareByID); + ImQsort(Data.Data, (size_t)Data.Size, sizeof(ImGuiStoragePair), StaticFunc::PairComparerByID); } int ImGuiStorage::GetInt(ImGuiID key, int default_val) const @@ -2153,9 +2419,10 @@ static bool GetSkipItemForListClipping() return (g.CurrentTable ? g.CurrentTable->HostSkipItems : g.CurrentWindow->SkipItems); } -// Helper to calculate coarse clipping of large list of evenly sized items. -// NB: Prefer using the ImGuiListClipper higher-level helper if you can! Read comments and instructions there on how those use this sort of pattern. -// NB: 'items_count' is only used to clamp the result, if you don't know your count you can use INT_MAX +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS +// Legacy helper to calculate coarse clipping of large list of evenly sized items. +// This legacy API is not ideal because it assume we will return a single contiguous rectangle. +// Prefer using ImGuiListClipper which can returns non-contiguous ranges. void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end) { ImGuiContext& g = *GImGui; @@ -2173,21 +2440,24 @@ void ImGui::CalcListClipping(int items_count, float items_height, int* out_items return; } - // We create the union of the ClipRect and the NavScoringRect which at worst should be 1 page away from ClipRect - ImRect unclipped_rect = window->ClipRect; - if (g.NavMoveRequest) - unclipped_rect.Add(g.NavScoringRect); + // We create the union of the ClipRect and the scoring rect which at worst should be 1 page away from ClipRect + // We don't include g.NavId's rectangle in there (unless g.NavJustMovedToId is set) because the rectangle enlargement can get costly. + ImRect rect = window->ClipRect; + if (g.NavMoveScoringItems) + rect.Add(g.NavScoringNoClipRect); if (g.NavJustMovedToId && window->NavLastIds[0] == g.NavJustMovedToId) - unclipped_rect.Add(ImRect(window->Pos + window->NavRectRel[0].Min, window->Pos + window->NavRectRel[0].Max)); + rect.Add(WindowRectRelToAbs(window, window->NavRectRel[0])); // Could store and use NavJustMovedToRectRel const ImVec2 pos = window->DC.CursorPos; - int start = (int)((unclipped_rect.Min.y - pos.y) / items_height); - int end = (int)((unclipped_rect.Max.y - pos.y) / items_height); + int start = (int)((rect.Min.y - pos.y) / items_height); + int end = (int)((rect.Max.y - pos.y) / items_height); // When performing a navigation request, ensure we have one item extra in the direction we are moving to - if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Up) + // FIXME: Verify this works with tabbing + const bool is_nav_request = (g.NavMoveScoringItems && g.NavWindow && g.NavWindow->RootWindowForNav == window->RootWindowForNav); + if (is_nav_request && g.NavMoveClipDir == ImGuiDir_Up) start--; - if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Down) + if (is_nav_request && g.NavMoveClipDir == ImGuiDir_Down) end++; start = ImClamp(start, 0, items_count); @@ -2195,17 +2465,42 @@ void ImGui::CalcListClipping(int items_count, float items_height, int* out_items *out_items_display_start = start; *out_items_display_end = end; } +#endif + +static void ImGuiListClipper_SortAndFuseRanges(ImVector& ranges, int offset = 0) +{ + if (ranges.Size - offset <= 1) + return; + + // Helper to order ranges and fuse them together if possible (bubble sort is fine as we are only sorting 2-3 entries) + for (int sort_end = ranges.Size - offset - 1; sort_end > 0; --sort_end) + for (int i = offset; i < sort_end + offset; ++i) + if (ranges[i].Min > ranges[i + 1].Min) + ImSwap(ranges[i], ranges[i + 1]); + + // Now fuse ranges together as much as possible. + for (int i = 1 + offset; i < ranges.Size; i++) + { + IM_ASSERT(!ranges[i].PosToIndexConvert && !ranges[i - 1].PosToIndexConvert); + if (ranges[i - 1].Max < ranges[i].Min) + continue; + ranges[i - 1].Min = ImMin(ranges[i - 1].Min, ranges[i].Min); + ranges[i - 1].Max = ImMax(ranges[i - 1].Max, ranges[i].Max); + ranges.erase(ranges.Data + i); + i--; + } +} -static void SetCursorPosYAndSetupForPrevLine(float pos_y, float line_height) +static void ImGuiListClipper_SeekCursorAndSetupPrevLine(float pos_y, float line_height) { // Set cursor position and a few other things so that SetScrollHereY() and Columns() can work when seeking cursor. // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue. - // The clipper should probably have a 4th step to display the last item in a regular manner. + // The clipper should probably have a final step to display the last item in a regular manner, maybe with an opt-out flag for data sets which may have costly seek? ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; float off_y = pos_y - window->DC.CursorPos.y; window->DC.CursorPos.y = pos_y; - window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, pos_y); + window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, pos_y - g.Style.ItemSpacing.y); window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y - line_height; // Setting those fields so that SetScrollHereY() can properly function after the end of our clipper usage. window->DC.PrevLineSize.y = (line_height - g.Style.ItemSpacing.y); // If we end up needing more accurate data (to e.g. use SameLine) we may as well make the clipper have a fourth step to let user process and display the last item in their list. if (ImGuiOldColumns* columns = window->DC.CurrentColumns) @@ -2221,6 +2516,15 @@ static void SetCursorPosYAndSetupForPrevLine(float pos_y, float line_height) } } +static void ImGuiListClipper_SeekCursorForItem(ImGuiListClipper* clipper, int item_n) +{ + // StartPosY starts from ItemsFrozen hence the subtraction + // Perform the add and multiply with double to allow seeking through larger ranges + ImGuiListClipperData* data = (ImGuiListClipperData*)clipper->TempData; + float pos_y = (float)((double)clipper->StartPosY + data->LossynessOffset + (double)(item_n - data->ItemsFrozen) * clipper->ItemsHeight); + ImGuiListClipper_SeekCursorAndSetupPrevLine(pos_y, clipper->ItemsHeight); +} + ImGuiListClipper::ImGuiListClipper() { memset(this, 0, sizeof(*this)); @@ -2229,7 +2533,7 @@ ImGuiListClipper::ImGuiListClipper() ImGuiListClipper::~ImGuiListClipper() { - IM_ASSERT(ItemsCount == -1 && "Forgot to call End(), or to Step() until false?"); + End(); } // Use case A: Begin() called from constructor with items_height<0, then called again from Step() in StepNo 1 @@ -2247,28 +2551,54 @@ void ImGuiListClipper::Begin(int items_count, float items_height) StartPosY = window->DC.CursorPos.y; ItemsHeight = items_height; ItemsCount = items_count; - ItemsFrozen = 0; - StepNo = 0; DisplayStart = -1; DisplayEnd = 0; + + // Acquire temporary buffer + if (++g.ClipperTempDataStacked > g.ClipperTempData.Size) + g.ClipperTempData.resize(g.ClipperTempDataStacked, ImGuiListClipperData()); + ImGuiListClipperData* data = &g.ClipperTempData[g.ClipperTempDataStacked - 1]; + data->Reset(this); + data->LossynessOffset = window->DC.CursorStartPosLossyness.y; + TempData = data; } void ImGuiListClipper::End() { - if (ItemsCount < 0) // Already ended - return; - - // In theory here we should assert that ImGui::GetCursorPosY() == StartPosY + DisplayEnd * ItemsHeight, but it feels saner to just seek at the end and not assert/crash the user. - if (ItemsCount < INT_MAX && DisplayStart >= 0) - SetCursorPosYAndSetupForPrevLine(StartPosY + (ItemsCount - ItemsFrozen) * ItemsHeight, ItemsHeight); + // In theory here we should assert that we are already at the right position, but it seems saner to just seek at the end and not assert/crash the user. + ImGuiContext& g = *GImGui; + if (ItemsCount >= 0 && ItemsCount < INT_MAX && DisplayStart >= 0) + ImGuiListClipper_SeekCursorForItem(this, ItemsCount); ItemsCount = -1; - StepNo = 3; + + // Restore temporary buffer and fix back pointers which may be invalidated when nesting + if (ImGuiListClipperData* data = (ImGuiListClipperData*)TempData) + { + IM_ASSERT(data->ListClipper == this); + data->StepNo = data->Ranges.Size; + if (--g.ClipperTempDataStacked > 0) + { + data = &g.ClipperTempData[g.ClipperTempDataStacked - 1]; + data->ListClipper->TempData = data; + } + TempData = NULL; + } +} + +void ImGuiListClipper::ForceDisplayRangeByIndices(int item_min, int item_max) +{ + ImGuiListClipperData* data = (ImGuiListClipperData*)TempData; + IM_ASSERT(DisplayStart < 0); // Only allowed after Begin() and if there has not been a specified range yet. + IM_ASSERT(item_min <= item_max); + if (item_min < item_max) + data->Ranges.push_back(ImGuiListClipperRange::FromIndices(item_min, item_max)); } bool ImGuiListClipper::Step() { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; + ImGuiListClipperData* data = (ImGuiListClipperData*)TempData; ImGuiTable* table = g.CurrentTable; if (table && table->IsInsideRow) @@ -2276,95 +2606,117 @@ bool ImGuiListClipper::Step() // No items if (ItemsCount == 0 || GetSkipItemForListClipping()) + return (void)End(), false; + + // While we are in frozen row state, keep displaying items one by one, unclipped + // FIXME: Could be stored as a table-agnostic state. + if (data->StepNo == 0 && table != NULL && !table->IsUnfrozenRows) { - End(); - return false; + DisplayStart = data->ItemsFrozen; + DisplayEnd = data->ItemsFrozen + 1; + if (DisplayStart >= ItemsCount) + return (void)End(), false; + data->ItemsFrozen++; + return true; } // Step 0: Let you process the first element (regardless of it being visible or not, so we can measure the element height) - if (StepNo == 0) + bool calc_clipping = false; + if (data->StepNo == 0) { - // While we are in frozen row state, keep displaying items one by one, unclipped - // FIXME: Could be stored as a table-agnostic state. - if (table != NULL && !table->IsUnfrozenRows) - { - DisplayStart = ItemsFrozen; - DisplayEnd = ItemsFrozen + 1; - ItemsFrozen++; - return true; - } - StartPosY = window->DC.CursorPos.y; if (ItemsHeight <= 0.0f) { - // Submit the first item so we can measure its height (generally it is 0..1) - DisplayStart = ItemsFrozen; - DisplayEnd = ItemsFrozen + 1; - StepNo = 1; + // Submit the first item (or range) so we can measure its height (generally the first range is 0..1) + data->Ranges.push_front(ImGuiListClipperRange::FromIndices(data->ItemsFrozen, data->ItemsFrozen + 1)); + DisplayStart = ImMax(data->Ranges[0].Min, data->ItemsFrozen); + DisplayEnd = ImMin(data->Ranges[0].Max, ItemsCount); + if (DisplayStart == DisplayEnd) + return (void)End(), false; + data->StepNo = 1; return true; } - - // Already has item height (given by user in Begin): skip to calculating step - DisplayStart = DisplayEnd; - StepNo = 2; + calc_clipping = true; // If on the first step with known item height, calculate clipping. } - // Step 1: the clipper infer height from first element - if (StepNo == 1) + // Step 1: Let the clipper infer height from first range + if (ItemsHeight <= 0.0f) { - IM_ASSERT(ItemsHeight <= 0.0f); + IM_ASSERT(data->StepNo == 1); if (table) - { - const float pos_y1 = table->RowPosY1; // Using this instead of StartPosY to handle clipper straddling the frozen row - const float pos_y2 = table->RowPosY2; // Using this instead of CursorPos.y to take account of tallest cell. - ItemsHeight = pos_y2 - pos_y1; - window->DC.CursorPos.y = pos_y2; - } - else - { - ItemsHeight = window->DC.CursorPos.y - StartPosY; - } + IM_ASSERT(table->RowPosY1 == StartPosY && table->RowPosY2 == window->DC.CursorPos.y); + + ItemsHeight = (window->DC.CursorPos.y - StartPosY) / (float)(DisplayEnd - DisplayStart); + bool affected_by_floating_point_precision = ImIsFloatAboveGuaranteedIntegerPrecision(StartPosY) || ImIsFloatAboveGuaranteedIntegerPrecision(window->DC.CursorPos.y); + if (affected_by_floating_point_precision) + ItemsHeight = window->DC.PrevLineSize.y + g.Style.ItemSpacing.y; // FIXME: Technically wouldn't allow multi-line entries. + IM_ASSERT(ItemsHeight > 0.0f && "Unable to calculate item height! First item hasn't moved the cursor vertically!"); - StepNo = 2; + calc_clipping = true; // If item height had to be calculated, calculate clipping afterwards. } - // Reached end of list - if (DisplayEnd >= ItemsCount) + // Step 0 or 1: Calculate the actual ranges of visible elements. + const int already_submitted = DisplayEnd; + if (calc_clipping) { - End(); - return false; + if (g.LogEnabled) + { + // If logging is active, do not perform any clipping + data->Ranges.push_back(ImGuiListClipperRange::FromIndices(0, ItemsCount)); + } + else + { + // Add range selected to be included for navigation + const bool is_nav_request = (g.NavMoveScoringItems && g.NavWindow && g.NavWindow->RootWindowForNav == window->RootWindowForNav); + if (is_nav_request) + data->Ranges.push_back(ImGuiListClipperRange::FromPositions(g.NavScoringNoClipRect.Min.y, g.NavScoringNoClipRect.Max.y, 0, 0)); + if (is_nav_request && (g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) && g.NavTabbingDir == -1) + data->Ranges.push_back(ImGuiListClipperRange::FromIndices(ItemsCount - 1, ItemsCount)); + + // Add focused/active item + ImRect nav_rect_abs = ImGui::WindowRectRelToAbs(window, window->NavRectRel[0]); + if (g.NavId != 0 && window->NavLastIds[0] == g.NavId) + data->Ranges.push_back(ImGuiListClipperRange::FromPositions(nav_rect_abs.Min.y, nav_rect_abs.Max.y, 0, 0)); + + // Add visible range + const int off_min = (is_nav_request && g.NavMoveClipDir == ImGuiDir_Up) ? -1 : 0; + const int off_max = (is_nav_request && g.NavMoveClipDir == ImGuiDir_Down) ? 1 : 0; + data->Ranges.push_back(ImGuiListClipperRange::FromPositions(window->ClipRect.Min.y, window->ClipRect.Max.y, off_min, off_max)); + } + + // Convert position ranges to item index ranges + // - Very important: when a starting position is after our maximum item, we set Min to (ItemsCount - 1). This allows us to handle most forms of wrapping. + // - Due to how Selectable extra padding they tend to be "unaligned" with exact unit in the item list, + // which with the flooring/ceiling tend to lead to 2 items instead of one being submitted. + for (int i = 0; i < data->Ranges.Size; i++) + if (data->Ranges[i].PosToIndexConvert) + { + int m1 = (int)(((double)data->Ranges[i].Min - window->DC.CursorPos.y - data->LossynessOffset) / ItemsHeight); + int m2 = (int)((((double)data->Ranges[i].Max - window->DC.CursorPos.y - data->LossynessOffset) / ItemsHeight) + 0.999999f); + data->Ranges[i].Min = ImClamp(already_submitted + m1 + data->Ranges[i].PosToIndexOffsetMin, already_submitted, ItemsCount - 1); + data->Ranges[i].Max = ImClamp(already_submitted + m2 + data->Ranges[i].PosToIndexOffsetMax, data->Ranges[i].Min + 1, ItemsCount); + data->Ranges[i].PosToIndexConvert = false; + } + ImGuiListClipper_SortAndFuseRanges(data->Ranges, data->StepNo); } - // Step 2: calculate the actual range of elements to display, and position the cursor before the first element - if (StepNo == 2) + // Step 0+ (if item height is given in advance) or 1+: Display the next range in line. + if (data->StepNo < data->Ranges.Size) { - IM_ASSERT(ItemsHeight > 0.0f); - - int already_submitted = DisplayEnd; - ImGui::CalcListClipping(ItemsCount - already_submitted, ItemsHeight, &DisplayStart, &DisplayEnd); - DisplayStart += already_submitted; - DisplayEnd += already_submitted; - - // Seek cursor - if (DisplayStart > already_submitted) - SetCursorPosYAndSetupForPrevLine(StartPosY + (DisplayStart - ItemsFrozen) * ItemsHeight, ItemsHeight); - - StepNo = 3; + DisplayStart = ImMax(data->Ranges[data->StepNo].Min, already_submitted); + DisplayEnd = ImMin(data->Ranges[data->StepNo].Max, ItemsCount); + if (DisplayStart > already_submitted) //-V1051 + ImGuiListClipper_SeekCursorForItem(this, DisplayStart); + data->StepNo++; return true; } - // Step 3: the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd), + // After the last step: Let the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd), // Advance the cursor to the end of the list and then returns 'false' to end the loop. - if (StepNo == 3) - { - // Seek cursor - if (ItemsCount < INT_MAX) - SetCursorPosYAndSetupForPrevLine(StartPosY + (ItemsCount - ItemsFrozen) * ItemsHeight, ItemsHeight); // advance cursor - ItemsCount = -1; - return false; - } + if (ItemsCount < INT_MAX) + ImGuiListClipper_SeekCursorForItem(this, ItemsCount); + ItemsCount = -1; - IM_ASSERT(0); return false; } @@ -2454,6 +2806,7 @@ struct ImGuiStyleVarInfo static const ImGuiStyleVarInfo GStyleVarInfo[] = { { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) }, // ImGuiStyleVar_Alpha + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, DisabledAlpha) }, // ImGuiStyleVar_DisabledAlpha { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) }, // ImGuiStyleVar_WindowPadding { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) }, // ImGuiStyleVar_WindowRounding { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowBorderSize) }, // ImGuiStyleVar_WindowBorderSize @@ -2731,7 +3084,7 @@ void ImGui::RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, con int ellipsis_char_count = 1; if (ellipsis_char == (ImWchar)-1) { - ellipsis_char = (ImWchar)'.'; + ellipsis_char = font->DotChar; ellipsis_char_count = 3; } const ImFontGlyph* glyph = font->FindGlyph(ellipsis_char); @@ -2791,8 +3144,8 @@ void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, const float border_size = g.Style.FrameBorderSize; if (border && border_size > 0.0f) { - window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size); - window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size); + window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, 0, border_size); + window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, 0, border_size); } } @@ -2803,8 +3156,8 @@ void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding) const float border_size = g.Style.FrameBorderSize; if (border_size > 0.0f) { - window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size); - window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size); + window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, 0, border_size); + window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, 0, border_size); } } @@ -2830,13 +3183,13 @@ void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFl bool fully_visible = window->ClipRect.Contains(display_rect); if (!fully_visible) window->DrawList->PushClipRect(display_rect.Min, display_rect.Max); - window->DrawList->AddRect(display_rect.Min + ImVec2(THICKNESS * 0.5f, THICKNESS * 0.5f), display_rect.Max - ImVec2(THICKNESS * 0.5f, THICKNESS * 0.5f), GetColorU32(ImGuiCol_NavHighlight), rounding, ImDrawCornerFlags_All, THICKNESS); + window->DrawList->AddRect(display_rect.Min + ImVec2(THICKNESS * 0.5f, THICKNESS * 0.5f), display_rect.Max - ImVec2(THICKNESS * 0.5f, THICKNESS * 0.5f), GetColorU32(ImGuiCol_NavHighlight), rounding, 0, THICKNESS); if (!fully_visible) window->DrawList->PopClipRect(); } if (flags & ImGuiNavHighlightFlags_TypeThin) { - window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, ~0, 1.0f); + window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, 0, 1.0f); } } @@ -2872,8 +3225,7 @@ ImGuiWindow::~ImGuiWindow() { IM_ASSERT(DrawList == &DrawListInst); IM_DELETE(Name); - for (int i = 0; i != ColumnsStorage.Size; i++) - ColumnsStorage[i].~ImGuiOldColumns(); + ColumnsStorage.clear_destruct(); } ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end) @@ -2881,10 +3233,9 @@ ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end) ImGuiID seed = IDStack.back(); ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed); ImGui::KeepAliveID(id); -#ifdef IMGUI_ENABLE_TEST_ENGINE ImGuiContext& g = *GImGui; - IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end); -#endif + if (g.DebugHookIdInfo == id) + ImGui::DebugHookIdInfo(id, ImGuiDataType_String, str, str_end); return id; } @@ -2893,10 +3244,9 @@ ImGuiID ImGuiWindow::GetID(const void* ptr) ImGuiID seed = IDStack.back(); ImGuiID id = ImHashData(&ptr, sizeof(void*), seed); ImGui::KeepAliveID(id); -#ifdef IMGUI_ENABLE_TEST_ENGINE ImGuiContext& g = *GImGui; - IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_Pointer, ptr); -#endif + if (g.DebugHookIdInfo == id) + ImGui::DebugHookIdInfo(id, ImGuiDataType_Pointer, ptr, NULL); return id; } @@ -2905,10 +3255,9 @@ ImGuiID ImGuiWindow::GetID(int n) ImGuiID seed = IDStack.back(); ImGuiID id = ImHashData(&n, sizeof(n), seed); ImGui::KeepAliveID(id); -#ifdef IMGUI_ENABLE_TEST_ENGINE ImGuiContext& g = *GImGui; - IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_S32, (intptr_t)n); -#endif + if (g.DebugHookIdInfo == id) + ImGui::DebugHookIdInfo(id, ImGuiDataType_S32, (void*)(intptr_t)n, NULL); return id; } @@ -2916,10 +3265,9 @@ ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end) { ImGuiID seed = IDStack.back(); ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed); -#ifdef IMGUI_ENABLE_TEST_ENGINE ImGuiContext& g = *GImGui; - IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end); -#endif + if (g.DebugHookIdInfo == id) + ImGui::DebugHookIdInfo(id, ImGuiDataType_String, str, str_end); return id; } @@ -2927,10 +3275,9 @@ ImGuiID ImGuiWindow::GetIDNoKeepAlive(const void* ptr) { ImGuiID seed = IDStack.back(); ImGuiID id = ImHashData(&ptr, sizeof(void*), seed); -#ifdef IMGUI_ENABLE_TEST_ENGINE ImGuiContext& g = *GImGui; - IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_Pointer, ptr); -#endif + if (g.DebugHookIdInfo == id) + ImGui::DebugHookIdInfo(id, ImGuiDataType_Pointer, ptr, NULL); return id; } @@ -2938,10 +3285,9 @@ ImGuiID ImGuiWindow::GetIDNoKeepAlive(int n) { ImGuiID seed = IDStack.back(); ImGuiID id = ImHashData(&n, sizeof(n), seed); -#ifdef IMGUI_ENABLE_TEST_ENGINE ImGuiContext& g = *GImGui; - IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_S32, (intptr_t)n); -#endif + if (g.DebugHookIdInfo == id) + ImGui::DebugHookIdInfo(id, ImGuiDataType_S32, (void*)(intptr_t)n, NULL); return id; } @@ -2949,7 +3295,7 @@ ImGuiID ImGuiWindow::GetIDNoKeepAlive(int n) ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs) { ImGuiID seed = IDStack.back(); - const int r_rel[4] = { (int)(r_abs.Min.x - Pos.x), (int)(r_abs.Min.y - Pos.y), (int)(r_abs.Max.x - Pos.x), (int)(r_abs.Max.y - Pos.y) }; + ImRect r_rel = ImGui::WindowRectAbsToRel(this, r_abs); ImGuiID id = ImHashData(&r_rel, sizeof(r_rel), seed); ImGui::KeepAliveID(id); return id; @@ -3007,6 +3353,7 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) g.ActiveIdTimer = 0.0f; g.ActiveIdHasBeenPressedBefore = false; g.ActiveIdHasBeenEditedBefore = false; + g.ActiveIdMouseButton = -1; if (id != 0) { g.LastActiveId = id; @@ -3021,7 +3368,7 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) if (id) { g.ActiveIdIsAlive = id; - g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id || g.NavJustTabbedId == id || g.NavJustMovedToId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse; + g.ActiveIdSource = (g.NavActivateId == id || g.NavActivateInputId == id || g.NavJustMovedToId == id) ? (ImGuiInputSource)ImGuiInputSource_Nav : ImGuiInputSource_Mouse; } // Clear declaration of inputs claimed by the widget @@ -3029,7 +3376,7 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) g.ActiveIdUsingMouseWheel = false; g.ActiveIdUsingNavDirMask = 0x00; g.ActiveIdUsingNavInputMask = 0x00; - g.ActiveIdUsingKeyInputMask = 0x00; + g.ActiveIdUsingKeyInputMask.ClearAllBits(); } void ImGui::ClearActiveID() @@ -3072,7 +3419,7 @@ void ImGui::MarkItemEdited(ImGuiID id) //IM_ASSERT(g.CurrentWindow->DC.LastItemId == id); g.ActiveIdHasBeenEditedThisFrame = true; g.ActiveIdHasBeenEditedBefore = true; - g.CurrentWindow->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Edited; + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Edited; } static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags) @@ -3102,39 +3449,49 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; if (g.NavDisableMouseHover && !g.NavDisableHighlight) - return IsItemFocused(); + { + if ((g.LastItemData.InFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled)) + return false; + if (!IsItemFocused()) + return false; + } + else + { + // Test for bounding box overlap, as updated as ItemAdd() + ImGuiItemStatusFlags status_flags = g.LastItemData.StatusFlags; + if (!(status_flags & ImGuiItemStatusFlags_HoveredRect)) + return false; + IM_ASSERT((flags & (ImGuiHoveredFlags_AnyWindow | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_NoPopupHierarchy)) == 0); // Flags not supported by this function + + // Test if we are hovering the right window (our window could be behind another window) + // [2021/03/02] Reworked / reverted the revert, finally. Note we want e.g. BeginGroup/ItemAdd/EndGroup to work as well. (#3851) + // [2017/10/16] Reverted commit 344d48be3 and testing RootWindow instead. I believe it is correct to NOT test for RootWindow but this leaves us unable + // to use IsItemHovered() after EndChild() itself. Until a solution is found I believe reverting to the test from 2017/09/27 is safe since this was + // the test that has been running for a long while. + if (g.HoveredWindow != window && (status_flags & ImGuiItemStatusFlags_HoveredWindow) == 0) + if ((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0) + return false; - // Test for bounding box overlap, as updated as ItemAdd() - if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect)) - return false; - IM_ASSERT((flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) == 0); // Flags not supported by this function - - // Test if we are hovering the right window (our window could be behind another window) - // [2017/10/16] Reverted commit 344d48be3 and testing RootWindow instead. I believe it is correct to NOT test for RootWindow but this leaves us unable to use IsItemHovered() after EndChild() itself. - // Until a solution is found I believe reverting to the test from 2017/09/27 is safe since this was the test that has been running for a long while. - //if (g.HoveredWindow != window) - // return false; - if (g.HoveredRootWindow != window->RootWindow && !(flags & ImGuiHoveredFlags_AllowWhenOverlapped)) - return false; + // Test if another item is active (e.g. being dragged) + if ((flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) == 0) + if (g.ActiveId != 0 && g.ActiveId != g.LastItemData.ID && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId) + return false; - // Test if another item is active (e.g. being dragged) - if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) - if (g.ActiveId != 0 && g.ActiveId != window->DC.LastItemId && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId) + // Test if interactions on this window are blocked by an active popup or modal. + // The ImGuiHoveredFlags_AllowWhenBlockedByPopup flag will be tested here. + if (!IsWindowContentHoverable(window, flags)) return false; - // Test if interactions on this window are blocked by an active popup or modal. - // The ImGuiHoveredFlags_AllowWhenBlockedByPopup flag will be tested here. - if (!IsWindowContentHoverable(window, flags)) - return false; + // Test if the item is disabled + if ((g.LastItemData.InFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled)) + return false; - // Test if the item is disabled - if ((window->DC.ItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled)) - return false; + // Special handling for calling after Begin() which represent the title bar or tab. + // When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case. + if (g.LastItemData.ID == window->MoveId && window->WriteAccessed) + return false; + } - // Special handling for calling after Begin() which represent the title bar or tab. - // When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case. - if (window->DC.LastItemId == window->MoveId && window->WriteAccessed) - return false; return true; } @@ -3154,7 +3511,7 @@ bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id) return false; if (g.NavDisableMouseHover) return false; - if (!IsWindowContentHoverable(window, ImGuiHoveredFlags_None) || (window->DC.ItemFlags & ImGuiItemFlags_Disabled)) + if (!IsWindowContentHoverable(window, ImGuiHoveredFlags_None)) { g.HoveredIdDisabled = true; return false; @@ -3163,9 +3520,21 @@ bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id) // We exceptionally allow this function to be called with id==0 to allow using it for easy high-level // hover test in widgets code. We could also decide to split this function is two. if (id != 0) - { SetHoveredID(id); + // When disabled we'll return false but still set HoveredId + ImGuiItemFlags item_flags = (g.LastItemData.ID == id ? g.LastItemData.InFlags : g.CurrentItemFlags); + if (item_flags & ImGuiItemFlags_Disabled) + { + // Release active id if turning disabled + if (g.ActiveId == id) + ClearActiveID(); + g.HoveredIdDisabled = true; + return false; + } + + if (id != 0) + { // [DEBUG] Item Picker tool! // We perform the check here because SetHoveredID() is not frequently called (1~ time a frame), making // the cost of this tool near-zero. We can get slightly better call-stack and support picking non-hovered @@ -3180,68 +3549,26 @@ bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id) return true; } -bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged) +bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; if (!bb.Overlaps(window->ClipRect)) if (id == 0 || (id != g.ActiveId && id != g.NavId)) - if (clip_even_when_logged || !g.LogEnabled) + if (!g.LogEnabled) return true; return false; } // This is also inlined in ItemAdd() // Note: if ImGuiItemStatusFlags_HasDisplayRect is set, user needs to set window->DC.LastItemDisplayRect! -void ImGui::SetLastItemData(ImGuiWindow* window, ImGuiID item_id, ImGuiItemStatusFlags item_flags, const ImRect& item_rect) -{ - window->DC.LastItemId = item_id; - window->DC.LastItemStatusFlags = item_flags; - window->DC.LastItemRect = item_rect; -} - -// Process TAB/Shift+TAB. Be mindful that this function may _clear_ the ActiveID when tabbing out. -bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id) +void ImGui::SetLastItemData(ImGuiID item_id, ImGuiItemFlags in_flags, ImGuiItemStatusFlags item_flags, const ImRect& item_rect) { ImGuiContext& g = *GImGui; - - // Increment counters - const bool is_tab_stop = (window->DC.ItemFlags & (ImGuiItemFlags_NoTabStop | ImGuiItemFlags_Disabled)) == 0; - window->DC.FocusCounterRegular++; - if (is_tab_stop) - window->DC.FocusCounterTabStop++; - - // Process TAB/Shift-TAB to tab *OUT* of the currently focused item. - // (Note that we can always TAB out of a widget that doesn't allow tabbing in) - if (g.ActiveId == id && g.FocusTabPressed && !IsActiveIdUsingKey(ImGuiKey_Tab) && g.FocusRequestNextWindow == NULL) - { - g.FocusRequestNextWindow = window; - g.FocusRequestNextCounterTabStop = window->DC.FocusCounterTabStop + (g.IO.KeyShift ? (is_tab_stop ? -1 : 0) : +1); // Modulo on index will be applied at the end of frame once we've got the total counter of items. - } - - // Handle focus requests - if (g.FocusRequestCurrWindow == window) - { - if (window->DC.FocusCounterRegular == g.FocusRequestCurrCounterRegular) - return true; - if (is_tab_stop && window->DC.FocusCounterTabStop == g.FocusRequestCurrCounterTabStop) - { - g.NavJustTabbedId = id; - return true; - } - - // If another item is about to be focused, we clear our own active id - if (g.ActiveId == id) - ClearActiveID(); - } - - return false; -} - -void ImGui::FocusableItemUnregister(ImGuiWindow* window) -{ - window->DC.FocusCounterRegular--; - window->DC.FocusCounterTabStop--; + g.LastItemData.ID = item_id; + g.LastItemData.InFlags = in_flags; + g.LastItemData.StatusFlags = item_flags; + g.LastItemData.Rect = item_rect; } float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x) @@ -3273,7 +3600,7 @@ void* ImGui::MemAlloc(size_t size) { if (ImGuiContext* ctx = GImGui) ctx->IO.MetricsActiveAllocations++; - return GImAllocatorAllocFunc(size, GImAllocatorUserData); + return (*GImAllocatorAllocFunc)(size, GImAllocatorUserData); } // IM_FREE() == ImGui::MemFree() @@ -3282,7 +3609,7 @@ void ImGui::MemFree(void* ptr) if (ptr) if (ImGuiContext* ctx = GImGui) ctx->IO.MetricsActiveAllocations--; - return GImAllocatorFreeFunc(ptr, GImAllocatorUserData); + return (*GImAllocatorFreeFunc)(ptr, GImAllocatorUserData); } const char* ImGui::GetClipboardText() @@ -3319,13 +3646,21 @@ void ImGui::SetCurrentContext(ImGuiContext* ctx) #endif } -void ImGui::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data) +void ImGui::SetAllocatorFunctions(ImGuiMemAllocFunc alloc_func, ImGuiMemFreeFunc free_func, void* user_data) { GImAllocatorAllocFunc = alloc_func; GImAllocatorFreeFunc = free_func; GImAllocatorUserData = user_data; } +// This is provided to facilitate copying allocators from one static/DLL boundary to another (e.g. retrieve default allocator of your executable address space) +void ImGui::GetAllocatorFunctions(ImGuiMemAllocFunc* p_alloc_func, ImGuiMemFreeFunc* p_free_func, void** p_user_data) +{ + *p_alloc_func = GImAllocatorAllocFunc; + *p_free_func = GImAllocatorFreeFunc; + *p_user_data = GImAllocatorUserData; +} + ImGuiContext* ImGui::CreateContext(ImFontAtlas* shared_font_atlas) { ImGuiContext* ctx = IM_NEW(ImGuiContext)(shared_font_atlas); @@ -3346,11 +3681,23 @@ void ImGui::DestroyContext(ImGuiContext* ctx) } // No specific ordering/dependency support, will see as needed -void ImGui::AddContextHook(ImGuiContext* ctx, const ImGuiContextHook* hook) +ImGuiID ImGui::AddContextHook(ImGuiContext* ctx, const ImGuiContextHook* hook) { ImGuiContext& g = *ctx; - IM_ASSERT(hook->Callback != NULL); + IM_ASSERT(hook->Callback != NULL && hook->HookId == 0 && hook->Type != ImGuiContextHookType_PendingRemoval_); g.Hooks.push_back(*hook); + g.Hooks.back().HookId = ++g.HookIdNext; + return g.HookIdNext; +} + +// Deferred removal, avoiding issue with changing vector while iterating it +void ImGui::RemoveContextHook(ImGuiContext* ctx, ImGuiID hook_id) +{ + ImGuiContext& g = *ctx; + IM_ASSERT(hook_id != 0); + for (int n = 0; n < g.Hooks.Size; n++) + if (g.Hooks[n].HookId == hook_id) + g.Hooks[n].Type = ImGuiContextHookType_PendingRemoval_; } // Call context hooks (used by e.g. test engine) @@ -3373,7 +3720,8 @@ ImGuiIO& ImGui::GetIO() ImDrawData* ImGui::GetDrawData() { ImGuiContext& g = *GImGui; - return g.DrawData.Valid ? &g.DrawData : NULL; + ImGuiViewportP* viewport = g.Viewports[0]; + return viewport->DrawDataP.Valid ? &viewport->DrawDataP : NULL; } double ImGui::GetTime() @@ -3386,14 +3734,50 @@ int ImGui::GetFrameCount() return GImGui->FrameCount; } +static ImDrawList* GetViewportDrawList(ImGuiViewportP* viewport, size_t drawlist_no, const char* drawlist_name) +{ + // Create the draw list on demand, because they are not frequently used for all viewports + ImGuiContext& g = *GImGui; + IM_ASSERT(drawlist_no < IM_ARRAYSIZE(viewport->DrawLists)); + ImDrawList* draw_list = viewport->DrawLists[drawlist_no]; + if (draw_list == NULL) + { + draw_list = IM_NEW(ImDrawList)(&g.DrawListSharedData); + draw_list->_OwnerName = drawlist_name; + viewport->DrawLists[drawlist_no] = draw_list; + } + + // Our ImDrawList system requires that there is always a command + if (viewport->DrawListsLastFrame[drawlist_no] != g.FrameCount) + { + draw_list->_ResetForNewFrame(); + draw_list->PushTextureID(g.IO.Fonts->TexID); + draw_list->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size, false); + viewport->DrawListsLastFrame[drawlist_no] = g.FrameCount; + } + return draw_list; +} + +ImDrawList* ImGui::GetBackgroundDrawList(ImGuiViewport* viewport) +{ + return GetViewportDrawList((ImGuiViewportP*)viewport, 0, "##Background"); +} + ImDrawList* ImGui::GetBackgroundDrawList() { - return &GImGui->BackgroundDrawList; + ImGuiContext& g = *GImGui; + return GetBackgroundDrawList(g.Viewports[0]); +} + +ImDrawList* ImGui::GetForegroundDrawList(ImGuiViewport* viewport) +{ + return GetViewportDrawList((ImGuiViewportP*)viewport, 1, "##Foreground"); } ImDrawList* ImGui::GetForegroundDrawList() { - return &GImGui->ForegroundDrawList; + ImGuiContext& g = *GImGui; + return GetForegroundDrawList(g.Viewports[0]); } ImDrawListSharedData* ImGui::GetDrawListSharedData() @@ -3410,8 +3794,9 @@ void ImGui::StartMouseMovingWindow(ImGuiWindow* window) FocusWindow(window); SetActiveID(window->MoveId, window); g.NavDisableHighlight = true; + g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - window->RootWindow->Pos; g.ActiveIdNoClearOnFocusLoss = true; - g.ActiveIdClickOffset = g.IO.MousePos - window->RootWindow->Pos; + SetActiveIdUsingNavAndKeys(); bool can_move_window = true; if ((window->Flags & ImGuiWindowFlags_NoMove) || (window->RootWindow->Flags & ImGuiWindowFlags_NoMove)) @@ -3447,8 +3832,8 @@ void ImGui::UpdateMouseMovingWindowNewFrame() } else { - ClearActiveID(); g.MovingWindow = NULL; + ClearActiveID(); } } else @@ -3475,12 +3860,13 @@ void ImGui::UpdateMouseMovingWindowEndFrame() if (g.NavWindow && g.NavWindow->Appearing) return; - // Click on empty space to focus window and start moving (after we're done with all our widgets) + // Click on empty space to focus window and start moving + // (after we're done with all our widgets) if (g.IO.MouseClicked[0]) { // Handle the edge case of a popup being closed while clicking in its empty space. // If we try to focus it, FocusWindow() > ClosePopupsOverWindow() will accidentally close any parent popups because they are not linked together any more. - ImGuiWindow* root_window = g.HoveredRootWindow; + ImGuiWindow* root_window = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL; const bool is_closed_popup = root_window && (root_window->Flags & ImGuiWindowFlags_Popup) && !IsPopupOpen(root_window->PopupId, ImGuiPopupFlags_AnyPopupLevel); if (root_window != NULL && !is_closed_popup) @@ -3511,7 +3897,7 @@ void ImGui::UpdateMouseMovingWindowEndFrame() // Find the top-most window between HoveredWindow and the top-most Modal Window. // This is where we can trim the popup stack. ImGuiWindow* modal = GetTopMostPopupModal(); - bool hovered_window_above_modal = g.HoveredWindow && IsWindowAbove(g.HoveredWindow, modal); + bool hovered_window_above_modal = g.HoveredWindow && (modal == NULL || IsWindowAbove(g.HoveredWindow, modal)); ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal, true); } } @@ -3521,74 +3907,144 @@ static bool IsWindowActiveAndVisible(ImGuiWindow* window) return (window->Active) && (!window->Hidden); } -static void ImGui::UpdateMouseInputs() +static void ImGui::UpdateKeyboardInputs() { ImGuiContext& g = *GImGui; + ImGuiIO& io = g.IO; - // Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well) - if (IsMousePosValid(&g.IO.MousePos)) - g.IO.MousePos = g.LastValidMousePos = ImFloor(g.IO.MousePos); + // Synchronize io.KeyMods with individual modifiers io.KeyXXX bools + io.KeyMods = GetMergedKeyModFlags(); - // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta - if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MousePosPrev)) - g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev; + // Import legacy keys or verify they are not used +#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO + if (io.BackendUsingLegacyKeyArrays == 0) + { + // Backend used new io.AddKeyEvent() API: Good! Verify that old arrays are never written too. + for (int n = 0; n < IM_ARRAYSIZE(io.KeysDown); n++) + IM_ASSERT(io.KeysDown[n] == false && "Backend needs to either only use io.AddKeyEvent(), either only fill legacy io.KeysDown[] + io.KeyMap[]. Not both!"); + } else - g.IO.MouseDelta = ImVec2(0.0f, 0.0f); - if (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f) - g.NavDisableMouseHover = false; - - g.IO.MousePosPrev = g.IO.MousePos; - for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++) { - g.IO.MouseClicked[i] = g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] < 0.0f; - g.IO.MouseReleased[i] = !g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] >= 0.0f; - g.IO.MouseDownDurationPrev[i] = g.IO.MouseDownDuration[i]; - g.IO.MouseDownDuration[i] = g.IO.MouseDown[i] ? (g.IO.MouseDownDuration[i] < 0.0f ? 0.0f : g.IO.MouseDownDuration[i] + g.IO.DeltaTime) : -1.0f; - g.IO.MouseDoubleClicked[i] = false; - if (g.IO.MouseClicked[i]) - { - if ((float)(g.Time - g.IO.MouseClickedTime[i]) < g.IO.MouseDoubleClickTime) + if (g.FrameCount == 0) + for (int n = ImGuiKey_LegacyNativeKey_BEGIN; n < ImGuiKey_LegacyNativeKey_END; n++) + IM_ASSERT(g.IO.KeyMap[n] == -1 && "Backend is not allowed to write to io.KeyMap[0..511]!"); + + // Build reverse KeyMap (Named -> Legacy) + for (int n = ImGuiKey_NamedKey_BEGIN; n < ImGuiKey_NamedKey_END; n++) + if (io.KeyMap[n] != -1) { - ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f); - if (ImLengthSqr(delta_from_click_pos) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist) - g.IO.MouseDoubleClicked[i] = true; - g.IO.MouseClickedTime[i] = -g.IO.MouseDoubleClickTime * 2.0f; // Mark as "old enough" so the third click isn't turned into a double-click + IM_ASSERT(IsLegacyKey((ImGuiKey)io.KeyMap[n])); + io.KeyMap[io.KeyMap[n]] = n; } - else + + // Import legacy keys into new ones + for (int n = ImGuiKey_LegacyNativeKey_BEGIN; n < ImGuiKey_LegacyNativeKey_END; n++) + if (io.KeysDown[n] || io.BackendUsingLegacyKeyArrays == 1) { - g.IO.MouseClickedTime[i] = g.Time; + const ImGuiKey key = (ImGuiKey)(io.KeyMap[n] != -1 ? io.KeyMap[n] : n); + IM_ASSERT(io.KeyMap[n] == -1 || IsNamedKey(key)); + io.KeysData[key].Down = io.KeysDown[n]; + io.BackendUsingLegacyKeyArrays = 1; } - g.IO.MouseClickedPos[i] = g.IO.MousePos; - g.IO.MouseDownWasDoubleClick[i] = g.IO.MouseDoubleClicked[i]; - g.IO.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f); - g.IO.MouseDragMaxDistanceSqr[i] = 0.0f; + if (io.BackendUsingLegacyKeyArrays == 1) + { + io.KeysData[ImGuiKey_ModCtrl].Down = io.KeyCtrl; + io.KeysData[ImGuiKey_ModShift].Down = io.KeyShift; + io.KeysData[ImGuiKey_ModAlt].Down = io.KeyAlt; + io.KeysData[ImGuiKey_ModSuper].Down = io.KeySuper; } - else if (g.IO.MouseDown[i]) + } +#endif + + // Clear gamepad data if disabled + if ((io.BackendFlags & ImGuiBackendFlags_HasGamepad) == 0) + for (int i = ImGuiKey_Gamepad_BEGIN; i < ImGuiKey_Gamepad_END; i++) { - // Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold - ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f); - g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(delta_from_click_pos)); - g.IO.MouseDragMaxDistanceAbs[i].x = ImMax(g.IO.MouseDragMaxDistanceAbs[i].x, delta_from_click_pos.x < 0.0f ? -delta_from_click_pos.x : delta_from_click_pos.x); - g.IO.MouseDragMaxDistanceAbs[i].y = ImMax(g.IO.MouseDragMaxDistanceAbs[i].y, delta_from_click_pos.y < 0.0f ? -delta_from_click_pos.y : delta_from_click_pos.y); - } - if (!g.IO.MouseDown[i] && !g.IO.MouseReleased[i]) - g.IO.MouseDownWasDoubleClick[i] = false; - if (g.IO.MouseClicked[i]) // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation - g.NavDisableMouseHover = false; + io.KeysData[i - ImGuiKey_KeysData_OFFSET].Down = false; + io.KeysData[i - ImGuiKey_KeysData_OFFSET].AnalogValue = 0.0f; + } + + // Update keys + for (int i = 0; i < IM_ARRAYSIZE(io.KeysData); i++) + { + ImGuiKeyData& key_data = io.KeysData[i]; + key_data.DownDurationPrev = key_data.DownDuration; + key_data.DownDuration = key_data.Down ? (key_data.DownDuration < 0.0f ? 0.0f : key_data.DownDuration + io.DeltaTime) : -1.0f; } } -static void StartLockWheelingWindow(ImGuiWindow* window) +static void ImGui::UpdateMouseInputs() { ImGuiContext& g = *GImGui; - if (g.WheelingWindow == window) - return; - g.WheelingWindow = window; - g.WheelingWindowRefMousePos = g.IO.MousePos; - g.WheelingWindowTimer = WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER; -} + ImGuiIO& io = g.IO; -void ImGui::UpdateMouseWheel() + // Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well) + if (IsMousePosValid(&io.MousePos)) + io.MousePos = g.MouseLastValidPos = ImFloorSigned(io.MousePos); + + // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta + if (IsMousePosValid(&io.MousePos) && IsMousePosValid(&io.MousePosPrev)) + io.MouseDelta = io.MousePos - io.MousePosPrev; + else + io.MouseDelta = ImVec2(0.0f, 0.0f); + + // If mouse moved we re-enable mouse hovering in case it was disabled by gamepad/keyboard. In theory should use a >0.0f threshold but would need to reset in everywhere we set this to true. + if (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f) + g.NavDisableMouseHover = false; + + io.MousePosPrev = io.MousePos; + for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) + { + io.MouseClicked[i] = io.MouseDown[i] && io.MouseDownDuration[i] < 0.0f; + io.MouseClickedCount[i] = 0; // Will be filled below + io.MouseReleased[i] = !io.MouseDown[i] && io.MouseDownDuration[i] >= 0.0f; + io.MouseDownDurationPrev[i] = io.MouseDownDuration[i]; + io.MouseDownDuration[i] = io.MouseDown[i] ? (io.MouseDownDuration[i] < 0.0f ? 0.0f : io.MouseDownDuration[i] + io.DeltaTime) : -1.0f; + if (io.MouseClicked[i]) + { + bool is_repeated_click = false; + if ((float)(g.Time - io.MouseClickedTime[i]) < io.MouseDoubleClickTime) + { + ImVec2 delta_from_click_pos = IsMousePosValid(&io.MousePos) ? (io.MousePos - io.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f); + if (ImLengthSqr(delta_from_click_pos) < io.MouseDoubleClickMaxDist * io.MouseDoubleClickMaxDist) + is_repeated_click = true; + } + if (is_repeated_click) + io.MouseClickedLastCount[i]++; + else + io.MouseClickedLastCount[i] = 1; + io.MouseClickedTime[i] = g.Time; + io.MouseClickedPos[i] = io.MousePos; + io.MouseClickedCount[i] = io.MouseClickedLastCount[i]; + io.MouseDragMaxDistanceSqr[i] = 0.0f; + } + else if (io.MouseDown[i]) + { + // Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold + float delta_sqr_click_pos = IsMousePosValid(&io.MousePos) ? ImLengthSqr(io.MousePos - io.MouseClickedPos[i]) : 0.0f; + io.MouseDragMaxDistanceSqr[i] = ImMax(io.MouseDragMaxDistanceSqr[i], delta_sqr_click_pos); + } + + // We provide io.MouseDoubleClicked[] as a legacy service + io.MouseDoubleClicked[i] = (io.MouseClickedCount[i] == 2); + + // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation + if (io.MouseClicked[i]) + g.NavDisableMouseHover = false; + } +} + +static void StartLockWheelingWindow(ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + if (g.WheelingWindow == window) + return; + g.WheelingWindow = window; + g.WheelingWindowRefMousePos = g.IO.MousePos; + g.WheelingWindowTimer = WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER; +} + +void ImGui::UpdateMouseWheel() { ImGuiContext& g = *GImGui; @@ -3623,7 +4079,7 @@ void ImGui::UpdateMouseWheel() const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f); const float scale = new_font_scale / window->FontWindowScale; window->FontWindowScale = new_font_scale; - if (!(window->Flags & ImGuiWindowFlags_ChildWindow)) + if (window == window->RootWindow) { const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size; SetWindowPos(window, window->Pos + offset, 0); @@ -3635,10 +4091,17 @@ void ImGui::UpdateMouseWheel() // Mouse wheel scrolling // If a child window has the ImGuiWindowFlags_NoScrollWithMouse flag, we give a chance to scroll its parent + if (g.IO.KeyCtrl) + return; + + // As a standard behavior holding SHIFT while using Vertical Mouse Wheel triggers Horizontal scroll instead + // (we avoid doing it on OSX as it the OS input layer handles this already) + const bool swap_axis = g.IO.KeyShift && !g.IO.ConfigMacOSXBehaviors; + const float wheel_y = swap_axis ? 0.0f : g.IO.MouseWheel; + const float wheel_x = swap_axis ? g.IO.MouseWheel : g.IO.MouseWheelH; // Vertical Mouse Wheel scrolling - const float wheel_y = (g.IO.MouseWheel != 0.0f && !g.IO.KeyShift) ? g.IO.MouseWheel : 0.0f; - if (wheel_y != 0.0f && !g.IO.KeyCtrl) + if (wheel_y != 0.0f) { StartLockWheelingWindow(window); while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.y == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs)))) @@ -3652,8 +4115,7 @@ void ImGui::UpdateMouseWheel() } // Horizontal Mouse Wheel scrolling, or Vertical Mouse Wheel w/ Shift held - const float wheel_x = (g.IO.MouseWheelH != 0.0f && !g.IO.KeyShift) ? g.IO.MouseWheelH : (g.IO.MouseWheel != 0.0f && g.IO.KeyShift) ? g.IO.MouseWheel : 0.0f; - if (wheel_x != 0.0f && !g.IO.KeyCtrl) + if (wheel_x != 0.0f) { StartLockWheelingWindow(window); while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.x == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs)))) @@ -3667,46 +4129,12 @@ void ImGui::UpdateMouseWheel() } } -void ImGui::UpdateTabFocus() -{ - ImGuiContext& g = *GImGui; - - // Pressing TAB activate widget focus - g.FocusTabPressed = (g.NavWindow && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab)); - if (g.ActiveId == 0 && g.FocusTabPressed) - { - // Note that SetKeyboardFocusHere() sets the Next fields mid-frame. To be consistent we also - // manipulate the Next fields even, even though they will be turned into Curr fields by the code below. - g.FocusRequestNextWindow = g.NavWindow; - g.FocusRequestNextCounterRegular = INT_MAX; - if (g.NavId != 0 && g.NavIdTabCounter != INT_MAX) - g.FocusRequestNextCounterTabStop = g.NavIdTabCounter + 1 + (g.IO.KeyShift ? -1 : 1); - else - g.FocusRequestNextCounterTabStop = g.IO.KeyShift ? -1 : 0; - } - - // Turn queued focus request into current one - g.FocusRequestCurrWindow = NULL; - g.FocusRequestCurrCounterRegular = g.FocusRequestCurrCounterTabStop = INT_MAX; - if (g.FocusRequestNextWindow != NULL) - { - ImGuiWindow* window = g.FocusRequestNextWindow; - g.FocusRequestCurrWindow = window; - if (g.FocusRequestNextCounterRegular != INT_MAX && window->DC.FocusCounterRegular != -1) - g.FocusRequestCurrCounterRegular = ImModPositive(g.FocusRequestNextCounterRegular, window->DC.FocusCounterRegular + 1); - if (g.FocusRequestNextCounterTabStop != INT_MAX && window->DC.FocusCounterTabStop != -1) - g.FocusRequestCurrCounterTabStop = ImModPositive(g.FocusRequestNextCounterTabStop, window->DC.FocusCounterTabStop + 1); - g.FocusRequestNextWindow = NULL; - g.FocusRequestNextCounterRegular = g.FocusRequestNextCounterTabStop = INT_MAX; - } - - g.NavIdTabCounter = INT_MAX; -} - // The reason this is exposed in imgui_internal.h is: on touch-based system that don't have hovering, we want to dispatch inputs to the right target (imgui vs imgui+app) void ImGui::UpdateHoveredWindowAndCaptureFlags() { ImGuiContext& g = *GImGui; + ImGuiIO& io = g.IO; + g.WindowsHoverPadding = ImMax(g.Style.TouchExtraPadding, ImVec2(WINDOWS_HOVER_PADDING, WINDOWS_HOVER_PADDING)); // Find the window hovered by mouse: // - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow. @@ -3717,52 +4145,65 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags() // Modal windows prevents mouse from hovering behind them. ImGuiWindow* modal_window = GetTopMostPopupModal(); - if (modal_window && g.HoveredRootWindow && !IsWindowChildOf(g.HoveredRootWindow, modal_window)) + if (modal_window && g.HoveredWindow && !IsWindowWithinBeginStackOf(g.HoveredWindow->RootWindow, modal_window)) clear_hovered_windows = true; // Disabled mouse? - if (g.IO.ConfigFlags & ImGuiConfigFlags_NoMouse) + if (io.ConfigFlags & ImGuiConfigFlags_NoMouse) clear_hovered_windows = true; - // We track click ownership. When clicked outside of a window the click is owned by the application and won't report hovering nor request capture even while dragging over our windows afterward. - int mouse_earliest_button_down = -1; + // We track click ownership. When clicked outside of a window the click is owned by the application and + // won't report hovering nor request capture even while dragging over our windows afterward. + const bool has_open_popup = (g.OpenPopupStack.Size > 0); + const bool has_open_modal = (modal_window != NULL); + int mouse_earliest_down = -1; bool mouse_any_down = false; - for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++) + for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) { - if (g.IO.MouseClicked[i]) - g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL) || (g.OpenPopupStack.Size > 0); - mouse_any_down |= g.IO.MouseDown[i]; - if (g.IO.MouseDown[i]) - if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[i] < g.IO.MouseClickedTime[mouse_earliest_button_down]) - mouse_earliest_button_down = i; + if (io.MouseClicked[i]) + { + io.MouseDownOwned[i] = (g.HoveredWindow != NULL) || has_open_popup; + io.MouseDownOwnedUnlessPopupClose[i] = (g.HoveredWindow != NULL) || has_open_modal; + } + mouse_any_down |= io.MouseDown[i]; + if (io.MouseDown[i]) + if (mouse_earliest_down == -1 || io.MouseClickedTime[i] < io.MouseClickedTime[mouse_earliest_down]) + mouse_earliest_down = i; } - const bool mouse_avail_to_imgui = (mouse_earliest_button_down == -1) || g.IO.MouseDownOwned[mouse_earliest_button_down]; + const bool mouse_avail = (mouse_earliest_down == -1) || io.MouseDownOwned[mouse_earliest_down]; + const bool mouse_avail_unless_popup_close = (mouse_earliest_down == -1) || io.MouseDownOwnedUnlessPopupClose[mouse_earliest_down]; // If mouse was first clicked outside of ImGui bounds we also cancel out hovering. // FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02) const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0; - if (!mouse_avail_to_imgui && !mouse_dragging_extern_payload) + if (!mouse_avail && !mouse_dragging_extern_payload) clear_hovered_windows = true; if (clear_hovered_windows) - g.HoveredWindow = g.HoveredRootWindow = g.HoveredWindowUnderMovingWindow = NULL; + g.HoveredWindow = g.HoveredWindowUnderMovingWindow = NULL; - // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to imgui, false = dispatch mouse info to Dear ImGui + app) + // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to Dear ImGui only, false = dispatch mouse to Dear ImGui + underlying app) + // Update io.WantCaptureMouseAllowPopupClose (experimental) to give a chance for app to react to popup closure with a drag if (g.WantCaptureMouseNextFrame != -1) - g.IO.WantCaptureMouse = (g.WantCaptureMouseNextFrame != 0); + { + io.WantCaptureMouse = io.WantCaptureMouseUnlessPopupClose = (g.WantCaptureMouseNextFrame != 0); + } else - g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (g.OpenPopupStack.Size > 0); + { + io.WantCaptureMouse = (mouse_avail && (g.HoveredWindow != NULL || mouse_any_down)) || has_open_popup; + io.WantCaptureMouseUnlessPopupClose = (mouse_avail_unless_popup_close && (g.HoveredWindow != NULL || mouse_any_down)) || has_open_modal; + } - // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to imgui, false = dispatch keyboard info to Dear ImGui + app) + // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to Dear ImGui only, false = dispatch keyboard info to Dear ImGui + underlying app) if (g.WantCaptureKeyboardNextFrame != -1) - g.IO.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0); + io.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0); else - g.IO.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL); - if (g.IO.NavActive && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard)) - g.IO.WantCaptureKeyboard = true; + io.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL); + if (io.NavActive && (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(io.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard)) + io.WantCaptureKeyboard = true; // Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible - g.IO.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false; + io.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false; } ImGuiKeyModFlags ImGui::GetMergedKeyModFlags() @@ -3781,6 +4222,12 @@ void ImGui::NewFrame() IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?"); ImGuiContext& g = *GImGui; + // Remove pending delete hooks before frame start. + // This deferred removal avoid issues of removal while iterating the hook vector + for (int n = g.Hooks.Size - 1; n >= 0; n--) + if (g.Hooks[n].Type == ImGuiContextHookType_PendingRemoval_) + g.Hooks.erase(&g.Hooks[n]); + CallContextHooks(&g, ImGuiContextHookType_NewFramePre); // Check and assert for various common IO and Configuration mistakes @@ -3800,15 +4247,21 @@ void ImGui::NewFrame() g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx]; g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime; g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame); - g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame))) : FLT_MAX; + g.FramerateSecPerFrameCount = ImMin(g.FramerateSecPerFrameCount + 1, IM_ARRAYSIZE(g.FramerateSecPerFrame)); + g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)g.FramerateSecPerFrameCount)) : FLT_MAX; + + UpdateViewportsNewFrame(); // Setup current font and draw list shared data g.IO.Fonts->Locked = true; SetCurrentFont(GetDefaultFont()); IM_ASSERT(g.Font->IsLoaded()); - g.DrawListSharedData.ClipRectFullscreen = ImVec4(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y); + ImRect virtual_space(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX); + for (int n = 0; n < g.Viewports.Size; n++) + virtual_space.Add(g.Viewports[n]->GetMainRect()); + g.DrawListSharedData.ClipRectFullscreen = virtual_space.ToVec4(); g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol; - g.DrawListSharedData.SetCircleSegmentMaxError(g.Style.CircleSegmentMaxError); + g.DrawListSharedData.SetCircleTessellationMaxError(g.Style.CircleTessellationMaxError); g.DrawListSharedData.InitialFlags = ImDrawListFlags_None; if (g.Style.AntiAliasedLines) g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLines; @@ -3819,16 +4272,12 @@ void ImGui::NewFrame() if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset) g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AllowVtxOffset; - g.BackgroundDrawList._ResetForNewFrame(); - g.BackgroundDrawList.PushTextureID(g.IO.Fonts->TexID); - g.BackgroundDrawList.PushClipRectFullScreen(); - - g.ForegroundDrawList._ResetForNewFrame(); - g.ForegroundDrawList.PushTextureID(g.IO.Fonts->TexID); - g.ForegroundDrawList.PushClipRectFullScreen(); - // Mark rendering data as invalid to prevent user who may have a handle on it to use it. - g.DrawData.Clear(); + for (int n = 0; n < g.Viewports.Size; n++) + { + ImGuiViewportP* viewport = g.Viewports[n]; + viewport->DrawDataP.Clear(); + } // Drag and drop keep the source ID alive so even if the source disappear our state is consistent if (g.DragDropActive && g.DragDropPayload.SourceId == g.ActiveId) @@ -3869,7 +4318,7 @@ void ImGui::NewFrame() { g.ActiveIdUsingNavDirMask = 0x00; g.ActiveIdUsingNavInputMask = 0x00; - g.ActiveIdUsingKeyInputMask = 0x00; + g.ActiveIdUsingKeyInputMask.ClearAllBits(); } // Drag and drop @@ -3880,12 +4329,21 @@ void ImGui::NewFrame() g.DragDropWithinTarget = false; g.DragDropHoldJustPressedId = 0; + // Close popups on focus lost (currently wip/opt-in) + //if (g.IO.AppFocusLost) + // ClosePopupsExceptModals(); + + // Process input queue (trickle as many events as possible) + g.InputEventsTrail.resize(0); + UpdateInputEvents(g.IO.ConfigInputTrickleEventQueue); + // Update keyboard input state - // Synchronize io.KeyMods with individual modifiers io.KeyXXX bools - g.IO.KeyMods = GetMergedKeyModFlags(); - memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration)); - for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++) - g.IO.KeysDownDuration[i] = g.IO.KeysDown[i] ? (g.IO.KeysDownDuration[i] < 0.0f ? 0.0f : g.IO.KeysDownDuration[i] + g.IO.DeltaTime) : -1.0f; + UpdateKeyboardInputs(); + + //IM_ASSERT(g.IO.KeyCtrl == IsKeyDown(ImGuiKey_LeftCtrl) || IsKeyDown(ImGuiKey_RightCtrl)); + //IM_ASSERT(g.IO.KeyShift == IsKeyDown(ImGuiKey_LeftShift) || IsKeyDown(ImGuiKey_RightShift)); + //IM_ASSERT(g.IO.KeyAlt == IsKeyDown(ImGuiKey_LeftAlt) || IsKeyDown(ImGuiKey_RightAlt)); + //IM_ASSERT(g.IO.KeySuper == IsKeyDown(ImGuiKey_LeftSuper) || IsKeyDown(ImGuiKey_RightSuper)); // Update gamepad/keyboard navigation NavUpdate(); @@ -3908,16 +4366,16 @@ void ImGui::NewFrame() g.MouseCursor = ImGuiMouseCursor_Arrow; g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1; - g.PlatformImePos = ImVec2(1.0f, 1.0f); // OS Input Method Editor showing on top-left of our window by default + + // Platform IME data: reset for the frame + g.PlatformImeDataPrev = g.PlatformImeData; + g.PlatformImeData.WantVisible = false; // Mouse wheel scrolling, scale UpdateMouseWheel(); - // Update legacy TAB focus - UpdateTabFocus(); - // Mark all windows as not visible and compact unused memory. - IM_ASSERT(g.WindowsFocusOrder.Size == g.Windows.Size); + IM_ASSERT(g.WindowsFocusOrder.Size <= g.Windows.Size); const float memory_compact_start_time = (g.GcCompactAll || g.IO.ConfigMemoryCompactTimer < 0.0f) ? FLT_MAX : (float)g.Time - g.IO.ConfigMemoryCompactTimer; for (int i = 0; i != g.Windows.Size; i++) { @@ -3936,6 +4394,9 @@ void ImGui::NewFrame() for (int i = 0; i < g.TablesLastTimeActive.Size; i++) if (g.TablesLastTimeActive[i] >= 0.0f && g.TablesLastTimeActive[i] < memory_compact_start_time) TableGcCompactTransientBuffers(g.Tables.GetByIndex(i)); + for (int i = 0; i < g.TablesTempData.Size; i++) + if (g.TablesTempData[i].LastTimeActive >= 0.0f && g.TablesTempData[i].LastTimeActive < memory_compact_start_time) + TableGcCompactTransientBuffers(&g.TablesTempData[i]); if (g.GcCompactAll) GcCompactTransientMiscBuffers(); g.GcCompactAll = false; @@ -3949,12 +4410,12 @@ void ImGui::NewFrame() g.CurrentWindowStack.resize(0); g.BeginPopupStack.resize(0); g.ItemFlagsStack.resize(0); - g.ItemFlagsStack.push_back(ImGuiItemFlags_Default_); + g.ItemFlagsStack.push_back(ImGuiItemFlags_None); g.GroupStack.resize(0); - ClosePopupsOverWindow(g.NavWindow, false); - // [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack. + // [DEBUG] Update debug features UpdateDebugToolItemPicker(); + UpdateDebugToolStackQueries(); // Create implicit/fallback window - which we will only render it if the user has added something to it. // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags. @@ -3967,31 +4428,6 @@ void ImGui::NewFrame() CallContextHooks(&g, ImGuiContextHookType_NewFramePost); } -// [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack. -void ImGui::UpdateDebugToolItemPicker() -{ - ImGuiContext& g = *GImGui; - g.DebugItemPickerBreakId = 0; - if (g.DebugItemPickerActive) - { - const ImGuiID hovered_id = g.HoveredIdPreviousFrame; - ImGui::SetMouseCursor(ImGuiMouseCursor_Hand); - if (ImGui::IsKeyPressedMap(ImGuiKey_Escape)) - g.DebugItemPickerActive = false; - if (ImGui::IsMouseClicked(0) && hovered_id) - { - g.DebugItemPickerBreakId = hovered_id; - g.DebugItemPickerActive = false; - } - ImGui::SetNextWindowBgAlpha(0.60f); - ImGui::BeginTooltip(); - ImGui::Text("HoveredId: 0x%08X", hovered_id); - ImGui::Text("Press ESC to abort picking."); - ImGui::TextColored(GetStyleColorVec4(hovered_id ? ImGuiCol_Text : ImGuiCol_TextDisabled), "Click to break in debugger!"); - ImGui::EndTooltip(); - } -} - void ImGui::Initialize(ImGuiContext* context) { ImGuiContext& g = *context; @@ -4010,13 +4446,15 @@ void ImGui::Initialize(ImGuiContext* context) g.SettingsHandlers.push_back(ini_handler); } -#ifdef IMGUI_HAS_TABLE // Add .ini handle for ImGuiTable type TableSettingsInstallHandler(context); -#endif // #ifdef IMGUI_HAS_TABLE + + // Create default viewport + ImGuiViewportP* viewport = IM_NEW(ImGuiViewportP)(); + g.Viewports.push_back(viewport); #ifdef IMGUI_HAS_DOCK -#endif // #ifdef IMGUI_HAS_DOCK +#endif g.Initialized = true; } @@ -4049,16 +4487,14 @@ void ImGui::Shutdown(ImGuiContext* context) CallContextHooks(&g, ImGuiContextHookType_Shutdown); // Clear everything else - for (int i = 0; i < g.Windows.Size; i++) - IM_DELETE(g.Windows[i]); - g.Windows.clear(); + g.Windows.clear_delete(); g.WindowsFocusOrder.clear(); g.WindowsTempSortBuffer.clear(); g.CurrentWindow = NULL; g.CurrentWindowStack.clear(); g.WindowsById.Clear(); g.NavWindow = NULL; - g.HoveredWindow = g.HoveredRootWindow = g.HoveredWindowUnderMovingWindow = NULL; + g.HoveredWindow = g.HoveredWindowUnderMovingWindow = NULL; g.ActiveIdWindow = g.ActiveIdPreviousFrameWindow = NULL; g.MovingWindow = NULL; g.ColorStack.clear(); @@ -4066,16 +4502,17 @@ void ImGui::Shutdown(ImGuiContext* context) g.FontStack.clear(); g.OpenPopupStack.clear(); g.BeginPopupStack.clear(); - g.DrawDataBuilder.ClearFreeMemory(); - g.BackgroundDrawList._ClearFreeMemory(); - g.ForegroundDrawList._ClearFreeMemory(); + + g.Viewports.clear_delete(); g.TabBars.Clear(); g.CurrentTabBarStack.clear(); g.ShrinkWidthBuffer.clear(); + g.ClipperTempData.clear_destruct(); + g.Tables.Clear(); - g.CurrentTableStack.clear(); + g.TablesTempData.clear_destruct(); g.DrawChannelsTempMergeBuffer.clear(); g.ClipboardHandlerData.clear(); @@ -4116,8 +4553,7 @@ static void AddWindowToSortBuffer(ImVector* out_sorted_windows, Im if (window->Active) { int count = window->DC.ChildWindows.Size; - if (count > 1) - ImQsort(window->DC.ChildWindows.Data, (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer); + ImQsort(window->DC.ChildWindows.Data, (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer); for (int i = 0; i < count; i++) { ImGuiWindow* child = window->DC.ChildWindows[i]; @@ -4129,11 +4565,10 @@ static void AddWindowToSortBuffer(ImVector* out_sorted_windows, Im static void AddDrawListToDrawData(ImVector* out_list, ImDrawList* draw_list) { - // Remove trailing command if unused. - // Technically we could return directly instead of popping, but this make things looks neat in Metrics/Debugger window as well. - draw_list->_PopUnusedDrawCmd(); if (draw_list->CmdBuffer.Size == 0) return; + if (draw_list->CmdBuffer.Size == 1 && draw_list->CmdBuffer[0].ElemCount == 0 && draw_list->CmdBuffer[0].UserCallback == NULL) + return; // Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc. // May trigger for you if you are using PrimXXX functions incorrectly. @@ -4163,25 +4598,29 @@ static void AddDrawListToDrawData(ImVector* out_list, ImDrawList* d out_list->push_back(draw_list); } -static void AddWindowToDrawData(ImVector* out_render_list, ImGuiWindow* window) +static void AddWindowToDrawData(ImGuiWindow* window, int layer) { ImGuiContext& g = *GImGui; + ImGuiViewportP* viewport = g.Viewports[0]; g.IO.MetricsRenderWindows++; - AddDrawListToDrawData(out_render_list, window->DrawList); + AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[layer], window->DrawList); for (int i = 0; i < window->DC.ChildWindows.Size; i++) { ImGuiWindow* child = window->DC.ChildWindows[i]; - if (IsWindowActiveAndVisible(child)) // clipped children may have been marked not active - AddWindowToDrawData(out_render_list, child); + if (IsWindowActiveAndVisible(child)) // Clipped children may have been marked not active + AddWindowToDrawData(child, layer); } } +static inline int GetWindowDisplayLayer(ImGuiWindow* window) +{ + return (window->Flags & ImGuiWindowFlags_Tooltip) ? 1 : 0; +} + // Layer is locked for the root window, however child windows may use a different viewport (e.g. extruding menu) -static void AddRootWindowToDrawData(ImGuiWindow* window) +static inline void AddRootWindowToDrawData(ImGuiWindow* window) { - ImGuiContext& g = *GImGui; - int layer = (window->Flags & ImGuiWindowFlags_Tooltip) ? 1 : 0; - AddWindowToDrawData(&g.DrawDataBuilder.Layers[layer], window); + AddWindowToDrawData(window, GetWindowDisplayLayer(window)); } void ImDrawDataBuilder::FlattenIntoSingleLayer() @@ -4202,20 +4641,23 @@ void ImDrawDataBuilder::FlattenIntoSingleLayer() } } -static void SetupDrawData(ImVector* draw_lists, ImDrawData* draw_data) +static void SetupViewportDrawData(ImGuiViewportP* viewport, ImVector* draw_lists) { ImGuiIO& io = ImGui::GetIO(); + ImDrawData* draw_data = &viewport->DrawDataP; draw_data->Valid = true; draw_data->CmdLists = (draw_lists->Size > 0) ? draw_lists->Data : NULL; draw_data->CmdListsCount = draw_lists->Size; draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0; - draw_data->DisplayPos = ImVec2(0.0f, 0.0f); - draw_data->DisplaySize = io.DisplaySize; + draw_data->DisplayPos = viewport->Pos; + draw_data->DisplaySize = viewport->Size; draw_data->FramebufferScale = io.DisplayFramebufferScale; for (int n = 0; n < draw_lists->Size; n++) { - draw_data->TotalVtxCount += draw_lists->Data[n]->VtxBuffer.Size; - draw_data->TotalIdxCount += draw_lists->Data[n]->IdxBuffer.Size; + ImDrawList* draw_list = draw_lists->Data[n]; + draw_list->_PopUnusedDrawCmd(); + draw_data->TotalVtxCount += draw_list->VtxBuffer.Size; + draw_data->TotalIdxCount += draw_list->IdxBuffer.Size; } } @@ -4239,6 +4681,88 @@ void ImGui::PopClipRect() window->ClipRect = window->DrawList->_ClipRectStack.back(); } +static void ImGui::RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col) +{ + if ((col & IM_COL32_A_MASK) == 0) + return; + + ImGuiViewportP* viewport = (ImGuiViewportP*)GetMainViewport(); + ImRect viewport_rect = viewport->GetMainRect(); + + // Draw behind window by moving the draw command at the FRONT of the draw list + { + // We've already called AddWindowToDrawData() which called DrawList->ChannelsMerge() on DockNodeHost windows, + // and draw list have been trimmed already, hence the explicit recreation of a draw command if missing. + // FIXME: This is creating complication, might be simpler if we could inject a drawlist in drawdata at a given position and not attempt to manipulate ImDrawCmd order. + ImDrawList* draw_list = window->RootWindow->DrawList; + if (draw_list->CmdBuffer.Size == 0) + draw_list->AddDrawCmd(); + draw_list->PushClipRect(viewport_rect.Min - ImVec2(1, 1), viewport_rect.Max + ImVec2(1, 1), false); // Ensure ImDrawCmd are not merged + draw_list->AddRectFilled(viewport_rect.Min, viewport_rect.Max, col); + ImDrawCmd cmd = draw_list->CmdBuffer.back(); + IM_ASSERT(cmd.ElemCount == 6); + draw_list->CmdBuffer.pop_back(); + draw_list->CmdBuffer.push_front(cmd); + draw_list->PopClipRect(); + draw_list->AddDrawCmd(); // We need to create a command as CmdBuffer.back().IdxOffset won't be correct if we append to same command. + } +} + +ImGuiWindow* ImGui::FindBottomMostVisibleWindowWithinBeginStack(ImGuiWindow* parent_window) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* bottom_most_visible_window = parent_window; + for (int i = FindWindowDisplayIndex(parent_window); i >= 0; i--) + { + ImGuiWindow* window = g.Windows[i]; + if (window->Flags & ImGuiWindowFlags_ChildWindow) + continue; + if (!IsWindowWithinBeginStackOf(window, parent_window)) + break; + if (IsWindowActiveAndVisible(window) && GetWindowDisplayLayer(window) <= GetWindowDisplayLayer(parent_window)) + bottom_most_visible_window = window; + } + return bottom_most_visible_window; +} + +static void ImGui::RenderDimmedBackgrounds() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* modal_window = GetTopMostAndVisiblePopupModal(); + if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f) + return; + const bool dim_bg_for_modal = (modal_window != NULL); + const bool dim_bg_for_window_list = (g.NavWindowingTargetAnim != NULL && g.NavWindowingTargetAnim->Active); + if (!dim_bg_for_modal && !dim_bg_for_window_list) + return; + + if (dim_bg_for_modal) + { + // Draw dimming behind modal or a begin stack child, whichever comes first in draw order. + ImGuiWindow* dim_behind_window = FindBottomMostVisibleWindowWithinBeginStack(modal_window); + RenderDimmedBackgroundBehindWindow(dim_behind_window, GetColorU32(ImGuiCol_ModalWindowDimBg, g.DimBgRatio)); + } + else if (dim_bg_for_window_list) + { + // Draw dimming behind CTRL+Tab target window + RenderDimmedBackgroundBehindWindow(g.NavWindowingTargetAnim, GetColorU32(ImGuiCol_NavWindowingDimBg, g.DimBgRatio)); + + // Draw border around CTRL+Tab target window + ImGuiWindow* window = g.NavWindowingTargetAnim; + ImGuiViewport* viewport = GetMainViewport(); + float distance = g.FontSize; + ImRect bb = window->Rect(); + bb.Expand(distance); + if (bb.GetWidth() >= viewport->Size.x && bb.GetHeight() >= viewport->Size.y) + bb.Expand(-distance - 1.0f); // If a window fits the entire viewport, adjust its highlight inward + if (window->DrawList->CmdBuffer.Size == 0) + window->DrawList->AddDrawCmd(); + window->DrawList->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size); + window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), window->WindowRounding, 0, 3.0f); + window->DrawList->PopClipRect(); + } +} + // This is normally called by Render(). You may want to call it directly if you want to avoid calling Render() but the gain will be very minimal. void ImGui::EndFrame() { @@ -4254,12 +4778,9 @@ void ImGui::EndFrame() ErrorCheckEndFrameSanityChecks(); - // Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME) - if (g.IO.ImeSetInputScreenPosFn && (g.PlatformImeLastPos.x == FLT_MAX || ImLengthSqr(g.PlatformImeLastPos - g.PlatformImePos) > 0.0001f)) - { - g.IO.ImeSetInputScreenPosFn((int)g.PlatformImePos.x, (int)g.PlatformImePos.y); - g.PlatformImeLastPos = g.PlatformImePos; - } + // Notify Platform/OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME) + if (g.IO.SetPlatformImeDataFn && memcmp(&g.PlatformImeData, &g.PlatformImeDataPrev, sizeof(ImGuiPlatformImeData)) != 0) + g.IO.SetPlatformImeDataFn(GetMainViewport(), &g.PlatformImeData); // Hide implicit/fallback "Debug" window if it hasn't been used g.WithinFrameScopeWithImplicitWindow = false; @@ -4317,11 +4838,15 @@ void ImGui::EndFrame() // Clear Input data for next frame g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f; g.IO.InputQueueCharacters.resize(0); + g.IO.KeyModsPrev = g.IO.KeyMods; // doing it here is better than in NewFrame() as we'll tolerate backend writing to KeyMods. If we want to firmly disallow it we should detect it. memset(g.IO.NavInputs, 0, sizeof(g.IO.NavInputs)); CallContextHooks(&g, ImGuiContextHookType_EndFramePost); } +// Prepare the data for rendering so you can call GetDrawData() +// (As with anything within the ImGui:: namspace this doesn't touch your GPU or graphics API at all: +// it is the role of the ImGui_ImplXXXX_RenderDrawData() function provided by the renderer backend) void ImGui::Render() { ImGuiContext& g = *GImGui; @@ -4329,15 +4854,24 @@ void ImGui::Render() if (g.FrameCountEnded != g.FrameCount) EndFrame(); + const bool first_render_of_frame = (g.FrameCountRendered != g.FrameCount); g.FrameCountRendered = g.FrameCount; g.IO.MetricsRenderWindows = 0; - g.DrawDataBuilder.Clear(); CallContextHooks(&g, ImGuiContextHookType_RenderPre); - // Add background ImDrawList - if (!g.BackgroundDrawList.VtxBuffer.empty()) - AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.BackgroundDrawList); + // Add background ImDrawList (for each active viewport) + for (int n = 0; n != g.Viewports.Size; n++) + { + ImGuiViewportP* viewport = g.Viewports[n]; + viewport->DrawDataBuilder.Clear(); + if (viewport->DrawLists[0] != NULL) + AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[0], GetBackgroundDrawList(viewport)); + } + + // Draw modal/window whitening backgrounds + if (first_render_of_frame) + RenderDimmedBackgrounds(); // Add ImDrawList to render ImGuiWindow* windows_to_render_top_most[2]; @@ -4346,26 +4880,34 @@ void ImGui::Render() for (int n = 0; n != g.Windows.Size; n++) { ImGuiWindow* window = g.Windows[n]; + IM_MSVC_WARNING_SUPPRESS(6011); // Static Analysis false positive "warning C6011: Dereferencing NULL pointer 'window'" if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != windows_to_render_top_most[0] && window != windows_to_render_top_most[1]) AddRootWindowToDrawData(window); } for (int n = 0; n < IM_ARRAYSIZE(windows_to_render_top_most); n++) if (windows_to_render_top_most[n] && IsWindowActiveAndVisible(windows_to_render_top_most[n])) // NavWindowingTarget is always temporarily displayed as the top-most window AddRootWindowToDrawData(windows_to_render_top_most[n]); - g.DrawDataBuilder.FlattenIntoSingleLayer(); - // Draw software mouse cursor if requested - if (g.IO.MouseDrawCursor) - RenderMouseCursor(&g.ForegroundDrawList, g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor, IM_COL32_WHITE, IM_COL32_BLACK, IM_COL32(0, 0, 0, 48)); + // Setup ImDrawData structures for end-user + g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = 0; + for (int n = 0; n < g.Viewports.Size; n++) + { + ImGuiViewportP* viewport = g.Viewports[n]; + viewport->DrawDataBuilder.FlattenIntoSingleLayer(); + + // Draw software mouse cursor if requested by io.MouseDrawCursor flag + if (g.IO.MouseDrawCursor && first_render_of_frame) + RenderMouseCursor(GetForegroundDrawList(viewport), g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor, IM_COL32_WHITE, IM_COL32_BLACK, IM_COL32(0, 0, 0, 48)); - // Add foreground ImDrawList - if (!g.ForegroundDrawList.VtxBuffer.empty()) - AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.ForegroundDrawList); + // Add foreground ImDrawList (for each active viewport) + if (viewport->DrawLists[1] != NULL) + AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[0], GetForegroundDrawList(viewport)); - // Setup ImDrawData structure for end-user - SetupDrawData(&g.DrawDataBuilder.Layers[0], &g.DrawData); - g.IO.MetricsRenderVertices = g.DrawData.TotalVtxCount; - g.IO.MetricsRenderIndices = g.DrawData.TotalIdxCount; + SetupViewportDrawData(viewport, &viewport->DrawDataBuilder.Layers[0]); + ImDrawData* draw_data = &viewport->DrawDataP; + g.IO.MetricsRenderVertices += draw_data->TotalVtxCount; + g.IO.MetricsRenderIndices += draw_data->TotalIdxCount; + } CallContextHooks(&g, ImGuiContextHookType_RenderPost); } @@ -4389,7 +4931,11 @@ ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_tex ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL); // Round - text_size.x = IM_FLOOR(text_size.x + 0.95f); + // FIXME: This has been here since Dec 2015 (7b0bf230) but down the line we want this out. + // FIXME: Investigate using ceilf or e.g. + // - https://git.musl-libc.org/cgit/musl/tree/src/math/ceilf.c + // - https://embarkstudios.github.io/rust-gpu/api/src/libm/math/ceilf.rs.html + text_size.x = IM_FLOOR(text_size.x + 0.99999f); return text_size; } @@ -4408,10 +4954,11 @@ static void FindHoveredWindow() hovered_window = g.MovingWindow; ImVec2 padding_regular = g.Style.TouchExtraPadding; - ImVec2 padding_for_resize_from_edges = g.IO.ConfigWindowsResizeFromEdges ? ImMax(g.Style.TouchExtraPadding, ImVec2(WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS, WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS)) : padding_regular; + ImVec2 padding_for_resize = g.IO.ConfigWindowsResizeFromEdges ? g.WindowsHoverPadding : padding_regular; for (int i = g.Windows.Size - 1; i >= 0; i--) { ImGuiWindow* window = g.Windows[i]; + IM_MSVC_WARNING_SUPPRESS(28182); // [Static Analyzer] Dereferencing NULL pointer. if (!window->Active || window->Hidden) continue; if (window->Flags & ImGuiWindowFlags_NoMouseInputs) @@ -4422,7 +4969,7 @@ static void FindHoveredWindow() if (window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize)) bb.Expand(padding_regular); else - bb.Expand(padding_for_resize_from_edges); + bb.Expand(padding_for_resize); if (!bb.Contains(g.IO.MousePos)) continue; @@ -4438,6 +4985,7 @@ static void FindHoveredWindow() if (hovered_window == NULL) hovered_window = window; + IM_MSVC_WARNING_SUPPRESS(28182); // [Static Analyzer] Dereferencing NULL pointer. if (hovered_window_ignoring_moving_window == NULL && (!g.MovingWindow || window->RootWindow != g.MovingWindow->RootWindow)) hovered_window_ignoring_moving_window = window; if (hovered_window && hovered_window_ignoring_moving_window) @@ -4445,375 +4993,146 @@ static void FindHoveredWindow() } g.HoveredWindow = hovered_window; - g.HoveredRootWindow = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL; g.HoveredWindowUnderMovingWindow = hovered_window_ignoring_moving_window; } -// Test if mouse cursor is hovering given rectangle -// NB- Rectangle is clipped by our current clip setting -// NB- Expand the rectangle to be generous on imprecise inputs systems (g.Style.TouchExtraPadding) -bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip) +bool ImGui::IsItemActive() { ImGuiContext& g = *GImGui; + if (g.ActiveId) + return g.ActiveId == g.LastItemData.ID; + return false; +} - // Clip - ImRect rect_clipped(r_min, r_max); - if (clip) - rect_clipped.ClipWith(g.CurrentWindow->ClipRect); +bool ImGui::IsItemActivated() +{ + ImGuiContext& g = *GImGui; + if (g.ActiveId) + if (g.ActiveId == g.LastItemData.ID && g.ActiveIdPreviousFrame != g.LastItemData.ID) + return true; + return false; +} - // Expand for touch input - const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding); - if (!rect_for_touch.Contains(g.IO.MousePos)) - return false; - return true; +bool ImGui::IsItemDeactivated() +{ + ImGuiContext& g = *GImGui; + if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasDeactivated) + return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Deactivated) != 0; + return (g.ActiveIdPreviousFrame == g.LastItemData.ID && g.ActiveIdPreviousFrame != 0 && g.ActiveId != g.LastItemData.ID); } -int ImGui::GetKeyIndex(ImGuiKey imgui_key) +bool ImGui::IsItemDeactivatedAfterEdit() { - IM_ASSERT(imgui_key >= 0 && imgui_key < ImGuiKey_COUNT); ImGuiContext& g = *GImGui; - return g.IO.KeyMap[imgui_key]; + return IsItemDeactivated() && (g.ActiveIdPreviousFrameHasBeenEditedBefore || (g.ActiveId == 0 && g.ActiveIdHasBeenEditedBefore)); } -// Note that dear imgui doesn't know the semantic of each entry of io.KeysDown[]! -// Use your own indices/enums according to how your backend/engine stored them into io.KeysDown[]! -bool ImGui::IsKeyDown(int user_key_index) +// == GetItemID() == GetFocusID() +bool ImGui::IsItemFocused() { - if (user_key_index < 0) - return false; ImGuiContext& g = *GImGui; - IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown)); - return g.IO.KeysDown[user_key_index]; + if (g.NavId != g.LastItemData.ID || g.NavId == 0) + return false; + return true; } -// t0 = previous time (e.g.: g.Time - g.IO.DeltaTime) -// t1 = current time (e.g.: g.Time) -// An event is triggered at: -// t = 0.0f t = repeat_delay, t = repeat_delay + repeat_rate*N -int ImGui::CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate) +// Important: this can be useful but it is NOT equivalent to the behavior of e.g.Button()! +// Most widgets have specific reactions based on mouse-up/down state, mouse position etc. +bool ImGui::IsItemClicked(ImGuiMouseButton mouse_button) { - if (t1 == 0.0f) - return 1; - if (t0 >= t1) - return 0; - if (repeat_rate <= 0.0f) - return (t0 < repeat_delay) && (t1 >= repeat_delay); - const int count_t0 = (t0 < repeat_delay) ? -1 : (int)((t0 - repeat_delay) / repeat_rate); - const int count_t1 = (t1 < repeat_delay) ? -1 : (int)((t1 - repeat_delay) / repeat_rate); - const int count = count_t1 - count_t0; - return count; + return IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_None); } -int ImGui::GetKeyPressedAmount(int key_index, float repeat_delay, float repeat_rate) +bool ImGui::IsItemToggledOpen() { ImGuiContext& g = *GImGui; - if (key_index < 0) - return 0; - IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown)); - const float t = g.IO.KeysDownDuration[key_index]; - return CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, repeat_delay, repeat_rate); + return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_ToggledOpen) ? true : false; } -bool ImGui::IsKeyPressed(int user_key_index, bool repeat) +bool ImGui::IsItemToggledSelection() { ImGuiContext& g = *GImGui; - if (user_key_index < 0) - return false; - IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown)); - const float t = g.IO.KeysDownDuration[user_key_index]; - if (t == 0.0f) - return true; - if (repeat && t > g.IO.KeyRepeatDelay) - return GetKeyPressedAmount(user_key_index, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0; - return false; + return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_ToggledSelection) ? true : false; } -bool ImGui::IsKeyReleased(int user_key_index) +bool ImGui::IsAnyItemHovered() { ImGuiContext& g = *GImGui; - if (user_key_index < 0) return false; - IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown)); - return g.IO.KeysDownDurationPrev[user_key_index] >= 0.0f && !g.IO.KeysDown[user_key_index]; + return g.HoveredId != 0 || g.HoveredIdPreviousFrame != 0; } -bool ImGui::IsMouseDown(ImGuiMouseButton button) +bool ImGui::IsAnyItemActive() { ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); - return g.IO.MouseDown[button]; + return g.ActiveId != 0; } -bool ImGui::IsMouseClicked(ImGuiMouseButton button, bool repeat) +bool ImGui::IsAnyItemFocused() { ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); - const float t = g.IO.MouseDownDuration[button]; - if (t == 0.0f) - return true; - - if (repeat && t > g.IO.KeyRepeatDelay) - { - // FIXME: 2019/05/03: Our old repeat code was wrong here and led to doubling the repeat rate, which made it an ok rate for repeat on mouse hold. - int amount = CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate * 0.50f); - if (amount > 0) - return true; - } - return false; + return g.NavId != 0 && !g.NavDisableHighlight; } -bool ImGui::IsMouseReleased(ImGuiMouseButton button) +bool ImGui::IsItemVisible() { ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); - return g.IO.MouseReleased[button]; + return g.CurrentWindow->ClipRect.Overlaps(g.LastItemData.Rect); } -bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button) +bool ImGui::IsItemEdited() { ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); - return g.IO.MouseDoubleClicked[button]; + return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Edited) != 0; } -// Return if a mouse click/drag went past the given threshold. Valid to call during the MouseReleased frame. -// [Internal] This doesn't test if the button is pressed -bool ImGui::IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold) +// Allow last item to be overlapped by a subsequent item. Both may be activated during the same frame before the later one takes priority. +// FIXME: Although this is exposed, its interaction and ideal idiom with using ImGuiButtonFlags_AllowItemOverlap flag are extremely confusing, need rework. +void ImGui::SetItemAllowOverlap() { ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); - if (lock_threshold < 0.0f) - lock_threshold = g.IO.MouseDragThreshold; - return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold; + ImGuiID id = g.LastItemData.ID; + if (g.HoveredId == id) + g.HoveredIdAllowOverlap = true; + if (g.ActiveId == id) + g.ActiveIdAllowOverlap = true; } -bool ImGui::IsMouseDragging(ImGuiMouseButton button, float lock_threshold) +void ImGui::SetItemUsingMouseWheel() { ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); - if (!g.IO.MouseDown[button]) - return false; - return IsMouseDragPastThreshold(button, lock_threshold); + ImGuiID id = g.LastItemData.ID; + if (g.HoveredId == id) + g.HoveredIdUsingMouseWheel = true; + if (g.ActiveId == id) + g.ActiveIdUsingMouseWheel = true; } -ImVec2 ImGui::GetMousePos() +void ImGui::SetActiveIdUsingNavAndKeys() { ImGuiContext& g = *GImGui; - return g.IO.MousePos; + IM_ASSERT(g.ActiveId != 0); + g.ActiveIdUsingNavDirMask = ~(ImU32)0; + g.ActiveIdUsingNavInputMask = ~(ImU32)0; + g.ActiveIdUsingKeyInputMask.SetAllBits(); + NavMoveRequestCancel(); } -// NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed! -ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup() +ImVec2 ImGui::GetItemRectMin() { ImGuiContext& g = *GImGui; - if (g.BeginPopupStack.Size > 0) - return g.OpenPopupStack[g.BeginPopupStack.Size - 1].OpenMousePos; - return g.IO.MousePos; + return g.LastItemData.Rect.Min; } -// We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position. -bool ImGui::IsMousePosValid(const ImVec2* mouse_pos) -{ - // The assert is only to silence a false-positive in XCode Static Analysis. - // Because GImGui is not dereferenced in every code path, the static analyzer assume that it may be NULL (which it doesn't for other functions). - IM_ASSERT(GImGui != NULL); - const float MOUSE_INVALID = -256000.0f; - ImVec2 p = mouse_pos ? *mouse_pos : GImGui->IO.MousePos; - return p.x >= MOUSE_INVALID && p.y >= MOUSE_INVALID; -} - -bool ImGui::IsAnyMouseDown() -{ - ImGuiContext& g = *GImGui; - for (int n = 0; n < IM_ARRAYSIZE(g.IO.MouseDown); n++) - if (g.IO.MouseDown[n]) - return true; - return false; -} - -// Return the delta from the initial clicking position while the mouse button is clicked or was just released. -// This is locked and return 0.0f until the mouse moves past a distance threshold at least once. -// NB: This is only valid if IsMousePosValid(). backends in theory should always keep mouse position valid when dragging even outside the client window. -ImVec2 ImGui::GetMouseDragDelta(ImGuiMouseButton button, float lock_threshold) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); - if (lock_threshold < 0.0f) - lock_threshold = g.IO.MouseDragThreshold; - if (g.IO.MouseDown[button] || g.IO.MouseReleased[button]) - if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold) - if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MouseClickedPos[button])) - return g.IO.MousePos - g.IO.MouseClickedPos[button]; - return ImVec2(0.0f, 0.0f); -} - -void ImGui::ResetMouseDragDelta(ImGuiMouseButton button) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); - // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr - g.IO.MouseClickedPos[button] = g.IO.MousePos; -} - -ImGuiMouseCursor ImGui::GetMouseCursor() -{ - return GImGui->MouseCursor; -} - -void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type) -{ - GImGui->MouseCursor = cursor_type; -} - -void ImGui::CaptureKeyboardFromApp(bool capture) -{ - GImGui->WantCaptureKeyboardNextFrame = capture ? 1 : 0; -} - -void ImGui::CaptureMouseFromApp(bool capture) -{ - GImGui->WantCaptureMouseNextFrame = capture ? 1 : 0; -} - -bool ImGui::IsItemActive() -{ - ImGuiContext& g = *GImGui; - if (g.ActiveId) - { - ImGuiWindow* window = g.CurrentWindow; - return g.ActiveId == window->DC.LastItemId; - } - return false; -} - -bool ImGui::IsItemActivated() -{ - ImGuiContext& g = *GImGui; - if (g.ActiveId) - { - ImGuiWindow* window = g.CurrentWindow; - if (g.ActiveId == window->DC.LastItemId && g.ActiveIdPreviousFrame != window->DC.LastItemId) - return true; - } - return false; -} - -bool ImGui::IsItemDeactivated() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - if (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDeactivated) - return (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_Deactivated) != 0; - return (g.ActiveIdPreviousFrame == window->DC.LastItemId && g.ActiveIdPreviousFrame != 0 && g.ActiveId != window->DC.LastItemId); -} - -bool ImGui::IsItemDeactivatedAfterEdit() -{ - ImGuiContext& g = *GImGui; - return IsItemDeactivated() && (g.ActiveIdPreviousFrameHasBeenEditedBefore || (g.ActiveId == 0 && g.ActiveIdHasBeenEditedBefore)); -} - -// == GetItemID() == GetFocusID() -bool ImGui::IsItemFocused() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - if (g.NavId != window->DC.LastItemId || g.NavId == 0) - return false; - return true; -} - -bool ImGui::IsItemClicked(ImGuiMouseButton mouse_button) -{ - return IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_None); -} - -bool ImGui::IsItemToggledOpen() -{ - ImGuiContext& g = *GImGui; - return (g.CurrentWindow->DC.LastItemStatusFlags & ImGuiItemStatusFlags_ToggledOpen) ? true : false; -} - -bool ImGui::IsItemToggledSelection() -{ - ImGuiContext& g = *GImGui; - return (g.CurrentWindow->DC.LastItemStatusFlags & ImGuiItemStatusFlags_ToggledSelection) ? true : false; -} - -bool ImGui::IsAnyItemHovered() -{ - ImGuiContext& g = *GImGui; - return g.HoveredId != 0 || g.HoveredIdPreviousFrame != 0; -} - -bool ImGui::IsAnyItemActive() -{ - ImGuiContext& g = *GImGui; - return g.ActiveId != 0; -} - -bool ImGui::IsAnyItemFocused() -{ - ImGuiContext& g = *GImGui; - return g.NavId != 0 && !g.NavDisableHighlight; -} - -bool ImGui::IsItemVisible() -{ - ImGuiWindow* window = GetCurrentWindowRead(); - return window->ClipRect.Overlaps(window->DC.LastItemRect); -} - -bool ImGui::IsItemEdited() -{ - ImGuiWindow* window = GetCurrentWindowRead(); - return (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_Edited) != 0; -} - -// Allow last item to be overlapped by a subsequent item. Both may be activated during the same frame before the later one takes priority. -// FIXME: Although this is exposed, its interaction and ideal idiom with using ImGuiButtonFlags_AllowItemOverlap flag are extremely confusing, need rework. -void ImGui::SetItemAllowOverlap() -{ - ImGuiContext& g = *GImGui; - ImGuiID id = g.CurrentWindow->DC.LastItemId; - if (g.HoveredId == id) - g.HoveredIdAllowOverlap = true; - if (g.ActiveId == id) - g.ActiveIdAllowOverlap = true; -} - -void ImGui::SetItemUsingMouseWheel() +ImVec2 ImGui::GetItemRectMax() { ImGuiContext& g = *GImGui; - ImGuiID id = g.CurrentWindow->DC.LastItemId; - if (g.HoveredId == id) - g.HoveredIdUsingMouseWheel = true; - if (g.ActiveId == id) - g.ActiveIdUsingMouseWheel = true; -} - -ImVec2 ImGui::GetItemRectMin() -{ - ImGuiWindow* window = GetCurrentWindowRead(); - return window->DC.LastItemRect.Min; -} - -ImVec2 ImGui::GetItemRectMax() -{ - ImGuiWindow* window = GetCurrentWindowRead(); - return window->DC.LastItemRect.Max; + return g.LastItemData.Rect.Max; } ImVec2 ImGui::GetItemRectSize() -{ - ImGuiWindow* window = GetCurrentWindowRead(); - return window->DC.LastItemRect.GetSize(); -} - -static ImRect GetViewportRect() { ImGuiContext& g = *GImGui; - return ImRect(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y); + return g.LastItemData.Rect.GetSize(); } bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags) @@ -4856,7 +5175,7 @@ bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, b parent_window->DC.CursorPos = child_window->Pos; // Process navigation-in immediately so NavInit can run on first frame - if (g.NavActivateId == id && !(flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayerActiveMask != 0 || child_window->DC.NavHasScroll)) + if (g.NavActivateId == id && !(flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayersActiveMask != 0 || child_window->DC.NavHasScroll)) { FocusWindow(child_window); NavInitWindow(child_window, false); @@ -4903,13 +5222,13 @@ void ImGui::EndChild() ImGuiWindow* parent_window = g.CurrentWindow; ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz); ItemSize(sz); - if ((window->DC.NavLayerActiveMask != 0 || window->DC.NavHasScroll) && !(window->Flags & ImGuiWindowFlags_NavFlattened)) + if ((window->DC.NavLayersActiveMask != 0 || window->DC.NavHasScroll) && !(window->Flags & ImGuiWindowFlags_NavFlattened)) { ItemAdd(bb, window->ChildId); RenderNavHighlight(bb, window->ChildId); - // When browsing a window that has no activable items (scroll only) we keep a highlight on the child - if (window->DC.NavLayerActiveMask == 0 && window == g.NavWindow) + // When browsing a window that has no activable items (scroll only) we keep a highlight on the child (pass g.NavId to trick into always displaying) + if (window->DC.NavLayersActiveMask == 0 && window == g.NavWindow) RenderNavHighlight(ImRect(bb.Min - ImVec2(2, 2), bb.Max + ImVec2(2, 2)), g.NavId, ImGuiNavHighlightFlags_TypeThin); } else @@ -4917,8 +5236,11 @@ void ImGui::EndChild() // Not navigable into ItemAdd(bb, 0); } + if (g.HoveredWindow == window) + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow; } g.WithinEndChild = false; + g.LogLinePosY = -FLT_MAX; // To enforce a carriage return } // Helper to create a child window / scrolling region that looks like a normal widget frame. @@ -4968,6 +5290,29 @@ static void ApplyWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settin window->Collapsed = settings->Collapsed; } +static void UpdateWindowInFocusOrderList(ImGuiWindow* window, bool just_created, ImGuiWindowFlags new_flags) +{ + ImGuiContext& g = *GImGui; + + const bool new_is_explicit_child = (new_flags & ImGuiWindowFlags_ChildWindow) != 0; + const bool child_flag_changed = new_is_explicit_child != window->IsExplicitChild; + if ((just_created || child_flag_changed) && !new_is_explicit_child) + { + IM_ASSERT(!g.WindowsFocusOrder.contains(window)); + g.WindowsFocusOrder.push_back(window); + window->FocusOrder = (short)(g.WindowsFocusOrder.Size - 1); + } + else if (!just_created && child_flag_changed && new_is_explicit_child) + { + IM_ASSERT(g.WindowsFocusOrder[window->FocusOrder] == window); + for (int n = window->FocusOrder + 1; n < g.WindowsFocusOrder.Size; n++) + g.WindowsFocusOrder[n]->FocusOrder--; + g.WindowsFocusOrder.erase(g.WindowsFocusOrder.Data + window->FocusOrder); + window->FocusOrder = -1; + } + window->IsExplicitChild = new_is_explicit_child; +} + static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags) { ImGuiContext& g = *GImGui; @@ -4979,7 +5324,8 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags) g.WindowsById.SetVoidPtr(window->ID, window); // Default/arbitrary window position. Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window. - window->Pos = ImVec2(60, 60); + const ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + window->Pos = main_viewport->Pos + ImVec2(60, 60); // User can disable loading and saving of settings. Tooltip and child windows also don't store settings. if (!(flags & ImGuiWindowFlags_NoSavedSettings)) @@ -5006,17 +5352,19 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags) window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0); } - g.WindowsFocusOrder.push_back(window); if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus) g.Windows.push_front(window); // Quite slow but rare and only once else g.Windows.push_back(window); + UpdateWindowInFocusOrderList(window, true, window->Flags); + return window; } -static ImVec2 CalcWindowSizeAfterConstraint(ImGuiWindow* window, ImVec2 new_size) +static ImVec2 CalcWindowSizeAfterConstraint(ImGuiWindow* window, const ImVec2& size_desired) { ImGuiContext& g = *GImGui; + ImVec2 new_size = size_desired; if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint) { // Using -1,-1 on either X/Y axis to preserve the current size. @@ -5041,8 +5389,9 @@ static ImVec2 CalcWindowSizeAfterConstraint(ImGuiWindow* window, ImVec2 new_size if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize))) { ImGuiWindow* window_for_height = window; + const float decoration_up_height = window_for_height->TitleBarHeight() + window_for_height->MenuBarHeight(); new_size = ImMax(new_size, g.Style.WindowMinSize); - new_size.y = ImMax(new_size.y, window_for_height->TitleBarHeight() + window_for_height->MenuBarHeight() + ImMax(0.0f, g.Style.WindowRounding - 1.0f)); // Reduce artifacts with very small windows + new_size.y = ImMax(new_size.y, decoration_up_height + ImMax(0.0f, g.Style.WindowRounding - 1.0f)); // Reduce artifacts with very small windows } return new_size; } @@ -5071,9 +5420,9 @@ static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_cont { ImGuiContext& g = *GImGui; ImGuiStyle& style = g.Style; - ImVec2 size_decorations = ImVec2(0.0f, window->TitleBarHeight() + window->MenuBarHeight()); + const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight(); ImVec2 size_pad = window->WindowPadding * 2.0f; - ImVec2 size_desired = size_contents + size_pad + size_decorations; + ImVec2 size_desired = size_contents + size_pad + ImVec2(0.0f, decoration_up_height); if (window->Flags & ImGuiWindowFlags_Tooltip) { // Tooltip always resize @@ -5087,13 +5436,16 @@ static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_cont ImVec2 size_min = style.WindowMinSize; if (is_popup || is_menu) // Popups and menus bypass style.WindowMinSize by default, but we give then a non-zero minimum size to facilitate understanding problematic cases (e.g. empty popups) size_min = ImMin(size_min, ImVec2(4.0f, 4.0f)); - ImVec2 size_auto_fit = ImClamp(size_desired, size_min, ImMax(size_min, g.IO.DisplaySize - style.DisplaySafeAreaPadding * 2.0f)); + + // FIXME-VIEWPORT-WORKAREA: May want to use GetWorkSize() instead of Size depending on the type of windows? + ImVec2 avail_size = ImGui::GetMainViewport()->Size; + ImVec2 size_auto_fit = ImClamp(size_desired, size_min, ImMax(size_min, avail_size - style.DisplaySafeAreaPadding * 2.0f)); // When the window cannot fit all contents (either because of constraints, either because screen is too small), // we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding. ImVec2 size_auto_fit_after_constraint = CalcWindowSizeAfterConstraint(window, size_auto_fit); - bool will_have_scrollbar_x = (size_auto_fit_after_constraint.x - size_pad.x - size_decorations.x < size_contents.x && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar); - bool will_have_scrollbar_y = (size_auto_fit_after_constraint.y - size_pad.y - size_decorations.y < size_contents.y && !(window->Flags & ImGuiWindowFlags_NoScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysVerticalScrollbar); + bool will_have_scrollbar_x = (size_auto_fit_after_constraint.x - size_pad.x - 0.0f < size_contents.x && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar); + bool will_have_scrollbar_y = (size_auto_fit_after_constraint.y - size_pad.y - decoration_up_height < size_contents.y && !(window->Flags & ImGuiWindowFlags_NoScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysVerticalScrollbar); if (will_have_scrollbar_x) size_auto_fit.y += style.ScrollbarSize; if (will_have_scrollbar_y) @@ -5112,11 +5464,11 @@ ImVec2 ImGui::CalcWindowNextAutoFitSize(ImGuiWindow* window) return size_final; } -static ImGuiCol GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags) +static ImGuiCol GetWindowBgColorIdx(ImGuiWindow* window) { - if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) + if (window->Flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) return ImGuiCol_PopupBg; - if (flags & ImGuiWindowFlags_ChildWindow) + if (window->Flags & ImGuiWindowFlags_ChildWindow) return ImGuiCol_ChildBg; return ImGuiCol_WindowBg; } @@ -5135,53 +5487,64 @@ static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& co *out_size = size_constrained; } +// Data for resizing from corner struct ImGuiResizeGripDef { ImVec2 CornerPosN; ImVec2 InnerDir; int AngleMin12, AngleMax12; }; - static const ImGuiResizeGripDef resize_grip_def[4] = { - { ImVec2(1, 1), ImVec2(-1, -1), 0, 3 }, // Lower-right - { ImVec2(0, 1), ImVec2(+1, -1), 3, 6 }, // Lower-left - { ImVec2(0, 0), ImVec2(+1, +1), 6, 9 }, // Upper-left (Unused) - { ImVec2(1, 0), ImVec2(-1, +1), 9, 12 }, // Upper-right (Unused) + { ImVec2(1, 1), ImVec2(-1, -1), 0, 3 }, // Lower-right + { ImVec2(0, 1), ImVec2(+1, -1), 3, 6 }, // Lower-left + { ImVec2(0, 0), ImVec2(+1, +1), 6, 9 }, // Upper-left (Unused) + { ImVec2(1, 0), ImVec2(-1, +1), 9, 12 } // Upper-right (Unused) }; +// Data for resizing from borders struct ImGuiResizeBorderDef { ImVec2 InnerDir; - ImVec2 CornerPosN1, CornerPosN2; + ImVec2 SegmentN1, SegmentN2; float OuterAngle; }; - static const ImGuiResizeBorderDef resize_border_def[4] = { - { ImVec2(0, +1), ImVec2(0, 0), ImVec2(1, 0), IM_PI * 1.50f }, // Top + { ImVec2(+1, 0), ImVec2(0, 1), ImVec2(0, 0), IM_PI * 1.00f }, // Left { ImVec2(-1, 0), ImVec2(1, 0), ImVec2(1, 1), IM_PI * 0.00f }, // Right - { ImVec2(0, -1), ImVec2(1, 1), ImVec2(0, 1), IM_PI * 0.50f }, // Bottom - { ImVec2(+1, 0), ImVec2(0, 1), ImVec2(0, 0), IM_PI * 1.00f } // Left + { ImVec2(0, +1), ImVec2(0, 0), ImVec2(1, 0), IM_PI * 1.50f }, // Up + { ImVec2(0, -1), ImVec2(1, 1), ImVec2(0, 1), IM_PI * 0.50f } // Down }; static ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness) { ImRect rect = window->Rect(); - if (thickness == 0.0f) rect.Max -= ImVec2(1, 1); - if (border_n == 0) { return ImRect(rect.Min.x + perp_padding, rect.Min.y - thickness, rect.Max.x - perp_padding, rect.Min.y + thickness); } // Top - if (border_n == 1) { return ImRect(rect.Max.x - thickness, rect.Min.y + perp_padding, rect.Max.x + thickness, rect.Max.y - perp_padding); } // Right - if (border_n == 2) { return ImRect(rect.Min.x + perp_padding, rect.Max.y - thickness, rect.Max.x - perp_padding, rect.Max.y + thickness); } // Bottom - if (border_n == 3) { return ImRect(rect.Min.x - thickness, rect.Min.y + perp_padding, rect.Min.x + thickness, rect.Max.y - perp_padding); } // Left + if (thickness == 0.0f) + rect.Max -= ImVec2(1, 1); + if (border_n == ImGuiDir_Left) { return ImRect(rect.Min.x - thickness, rect.Min.y + perp_padding, rect.Min.x + thickness, rect.Max.y - perp_padding); } + if (border_n == ImGuiDir_Right) { return ImRect(rect.Max.x - thickness, rect.Min.y + perp_padding, rect.Max.x + thickness, rect.Max.y - perp_padding); } + if (border_n == ImGuiDir_Up) { return ImRect(rect.Min.x + perp_padding, rect.Min.y - thickness, rect.Max.x - perp_padding, rect.Min.y + thickness); } + if (border_n == ImGuiDir_Down) { return ImRect(rect.Min.x + perp_padding, rect.Max.y - thickness, rect.Max.x - perp_padding, rect.Max.y + thickness); } IM_ASSERT(0); return ImRect(); } // 0..3: corners (Lower-right, Lower-left, Unused, Unused) -// 4..7: borders (Top, Right, Bottom, Left) -ImGuiID ImGui::GetWindowResizeID(ImGuiWindow* window, int n) +ImGuiID ImGui::GetWindowResizeCornerID(ImGuiWindow* window, int n) +{ + IM_ASSERT(n >= 0 && n < 4); + ImGuiID id = window->ID; + id = ImHashStr("#RESIZE", 0, id); + id = ImHashData(&n, sizeof(int), id); + return id; +} + +// Borders (Left, Right, Up, Down) +ImGuiID ImGui::GetWindowResizeBorderID(ImGuiWindow* window, ImGuiDir dir) { - IM_ASSERT(n >= 0 && n <= 7); + IM_ASSERT(dir >= 0 && dir < 4); + int n = (int)dir + 4; ImGuiID id = window->ID; id = ImHashStr("#RESIZE", 0, id); id = ImHashData(&n, sizeof(int), id); @@ -5204,7 +5567,7 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s const int resize_border_count = g.IO.ConfigWindowsResizeFromEdges ? 4 : 0; const float grip_draw_size = IM_FLOOR(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f)); const float grip_hover_inner_size = IM_FLOOR(grip_draw_size * 0.75f); - const float grip_hover_outer_size = g.IO.ConfigWindowsResizeFromEdges ? WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS : 0.0f; + const float grip_hover_outer_size = g.IO.ConfigWindowsResizeFromEdges ? WINDOWS_HOVER_PADDING : 0.0f; ImVec2 pos_target(FLT_MAX, FLT_MAX); ImVec2 size_target(FLT_MAX, FLT_MAX); @@ -5216,20 +5579,21 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s PushID("#RESIZE"); for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++) { - const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n]; - const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN); + const ImGuiResizeGripDef& def = resize_grip_def[resize_grip_n]; + const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, def.CornerPosN); // Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window - ImRect resize_rect(corner - grip.InnerDir * grip_hover_outer_size, corner + grip.InnerDir * grip_hover_inner_size); + bool hovered, held; + ImRect resize_rect(corner - def.InnerDir * grip_hover_outer_size, corner + def.InnerDir * grip_hover_inner_size); if (resize_rect.Min.x > resize_rect.Max.x) ImSwap(resize_rect.Min.x, resize_rect.Max.x); if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(resize_rect.Min.y, resize_rect.Max.y); - bool hovered, held; - ButtonBehavior(resize_rect, window->GetID(resize_grip_n), &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus); + ImGuiID resize_grip_id = window->GetID(resize_grip_n); // == GetWindowResizeCornerID() + ButtonBehavior(resize_rect, resize_grip_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus); //GetForegroundDrawList(window)->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255)); if (hovered || held) g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE; - if (held && g.IO.MouseDoubleClicked[0] && resize_grip_n == 0) + if (held && g.IO.MouseClickedCount[0] == 2 && resize_grip_n == 0) { // Manual auto-fit when double-clicking size_target = CalcWindowSizeAfterConstraint(window, size_auto_fit); @@ -5240,39 +5604,41 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s { // Resize from any of the four corners // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position - ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + ImLerp(grip.InnerDir * grip_hover_outer_size, grip.InnerDir * -grip_hover_inner_size, grip.CornerPosN); // Corner of the window corresponding to our corner grip - ImVec2 clamp_min = ImVec2(grip.CornerPosN.x == 1.0f ? visibility_rect.Min.x : -FLT_MAX, grip.CornerPosN.y == 1.0f ? visibility_rect.Min.y : -FLT_MAX); - ImVec2 clamp_max = ImVec2(grip.CornerPosN.x == 0.0f ? visibility_rect.Max.x : +FLT_MAX, grip.CornerPosN.y == 0.0f ? visibility_rect.Max.y : +FLT_MAX); + ImVec2 clamp_min = ImVec2(def.CornerPosN.x == 1.0f ? visibility_rect.Min.x : -FLT_MAX, def.CornerPosN.y == 1.0f ? visibility_rect.Min.y : -FLT_MAX); + ImVec2 clamp_max = ImVec2(def.CornerPosN.x == 0.0f ? visibility_rect.Max.x : +FLT_MAX, def.CornerPosN.y == 0.0f ? visibility_rect.Max.y : +FLT_MAX); + ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + ImLerp(def.InnerDir * grip_hover_outer_size, def.InnerDir * -grip_hover_inner_size, def.CornerPosN); // Corner of the window corresponding to our corner grip corner_target = ImClamp(corner_target, clamp_min, clamp_max); - CalcResizePosSizeFromAnyCorner(window, corner_target, grip.CornerPosN, &pos_target, &size_target); + CalcResizePosSizeFromAnyCorner(window, corner_target, def.CornerPosN, &pos_target, &size_target); } + + // Only lower-left grip is visible before hovering/activating if (resize_grip_n == 0 || held || hovered) resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip); } for (int border_n = 0; border_n < resize_border_count; border_n++) { + const ImGuiResizeBorderDef& def = resize_border_def[border_n]; + const ImGuiAxis axis = (border_n == ImGuiDir_Left || border_n == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y; + bool hovered, held; - ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); - ButtonBehavior(border_rect, window->GetID(border_n + 4), &hovered, &held, ImGuiButtonFlags_FlattenChildren); + ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, WINDOWS_HOVER_PADDING); + ImGuiID border_id = window->GetID(border_n + 4); // == GetWindowResizeBorderID() + ButtonBehavior(border_rect, border_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus); //GetForegroundDrawLists(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255)); if ((hovered && g.HoveredIdTimer > WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER) || held) { - g.MouseCursor = (border_n & 1) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS; + g.MouseCursor = (axis == ImGuiAxis_X) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS; if (held) *border_held = border_n; } if (held) { + ImVec2 clamp_min(border_n == ImGuiDir_Right ? visibility_rect.Min.x : -FLT_MAX, border_n == ImGuiDir_Down ? visibility_rect.Min.y : -FLT_MAX); + ImVec2 clamp_max(border_n == ImGuiDir_Left ? visibility_rect.Max.x : +FLT_MAX, border_n == ImGuiDir_Up ? visibility_rect.Max.y : +FLT_MAX); ImVec2 border_target = window->Pos; - ImVec2 border_posn; - if (border_n == 0) { border_posn = ImVec2(0, 0); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Top - if (border_n == 1) { border_posn = ImVec2(1, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Right - if (border_n == 2) { border_posn = ImVec2(0, 1); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Bottom - if (border_n == 3) { border_posn = ImVec2(0, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Left - ImVec2 clamp_min = ImVec2(border_n == 1 ? visibility_rect.Min.x : -FLT_MAX, border_n == 2 ? visibility_rect.Min.y : -FLT_MAX); - ImVec2 clamp_max = ImVec2(border_n == 3 ? visibility_rect.Max.x : +FLT_MAX, border_n == 0 ? visibility_rect.Max.y : +FLT_MAX); + border_target[axis] = g.IO.MousePos[axis] - g.ActiveIdClickOffset[axis] + WINDOWS_HOVER_PADDING; border_target = ImClamp(border_target, clamp_min, clamp_max); - CalcResizePosSizeFromAnyCorner(window, border_target, border_posn, &pos_target, &size_target); + CalcResizePosSizeFromAnyCorner(window, border_target, ImMin(def.SegmentN1, def.SegmentN2), &pos_target, &size_target); } } PopID(); @@ -5284,9 +5650,9 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s if (g.NavWindowingTarget && g.NavWindowingTarget->RootWindow == window) { ImVec2 nav_resize_delta; - if (g.NavInputSource == ImGuiInputSource_NavKeyboard && g.IO.KeyShift) - nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down); - if (g.NavInputSource == ImGuiInputSource_NavGamepad) + if (g.NavInputSource == ImGuiInputSource_Keyboard && g.IO.KeyShift) + nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_RawKeyboard, ImGuiInputReadMode_Down); + if (g.NavInputSource == ImGuiInputSource_Gamepad) nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_Down); if (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f) { @@ -5332,16 +5698,16 @@ static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window) float rounding = window->WindowRounding; float border_size = window->WindowBorderSize; if (border_size > 0.0f && !(window->Flags & ImGuiWindowFlags_NoBackground)) - window->DrawList->AddRect(window->Pos, window->Pos + window->Size, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size); + window->DrawList->AddRect(window->Pos, window->Pos + window->Size, GetColorU32(ImGuiCol_Border), rounding, 0, border_size); int border_held = window->ResizeBorderHeld; if (border_held != -1) { const ImGuiResizeBorderDef& def = resize_border_def[border_held]; ImRect border_r = GetResizeBorderRect(window, border_held, rounding, 0.0f); - window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.CornerPosN1) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle - IM_PI * 0.25f, def.OuterAngle); - window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.CornerPosN2) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle, def.OuterAngle + IM_PI * 0.25f); - window->DrawList->PathStroke(GetColorU32(ImGuiCol_SeparatorActive), false, ImMax(2.0f, border_size)); // Thicker than usual + window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.SegmentN1) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle - IM_PI * 0.25f, def.OuterAngle); + window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.SegmentN2) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle, def.OuterAngle + IM_PI * 0.25f); + window->DrawList->PathStroke(GetColorU32(ImGuiCol_SeparatorActive), 0, ImMax(2.0f, border_size)); // Thicker than usual } if (g.Style.FrameBorderSize > 0 && !(window->Flags & ImGuiWindowFlags_NoTitleBar)) { @@ -5380,7 +5746,7 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar // Window background if (!(flags & ImGuiWindowFlags_NoBackground)) { - ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags)); + ImU32 bg_col = GetColorU32(GetWindowBgColorIdx(window)); bool override_alpha = false; float alpha = 1.0f; if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasBgAlpha) @@ -5390,14 +5756,14 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar } if (override_alpha) bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(alpha) << IM_COL32_A_SHIFT); - window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight()), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Bot); + window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight()), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? 0 : ImDrawFlags_RoundCornersBottom); } // Title bar if (!(flags & ImGuiWindowFlags_NoTitleBar)) { ImU32 title_bar_col = GetColorU32(title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg); - window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawCornerFlags_Top); + window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawFlags_RoundCornersTop); } // Menu bar @@ -5405,7 +5771,7 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar { ImRect menu_bar_rect = window->MenuBarRect(); menu_bar_rect.ClipWith(window->Rect()); // Soft clipping, in particular child window don't have minimum size covering the menu bar so this is useful for them. - window->DrawList->AddRectFilled(menu_bar_rect.Min + ImVec2(window_border_size, 0), menu_bar_rect.Max - ImVec2(window_border_size, 0), GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImDrawCornerFlags_Top); + window->DrawList->AddRectFilled(menu_bar_rect.Min + ImVec2(window_border_size, 0), menu_bar_rect.Max - ImVec2(window_border_size, 0), GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImDrawFlags_RoundCornersTop); if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y) window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.FrameBorderSize); } @@ -5446,8 +5812,9 @@ void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& titl const bool has_collapse_button = !(flags & ImGuiWindowFlags_NoCollapse) && (style.WindowMenuButtonPosition != ImGuiDir_None); // Close & Collapse button are on the Menu NavLayer and don't default focus (unless there's nothing else on that layer) - const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags; - window->DC.ItemFlags |= ImGuiItemFlags_NoNavDefaultFocus; + // FIXME-NAV: Might want (or not?) to set the equivalent of ImGuiButtonFlags_NoNavFocus so that mouse clicks on standard title bar items don't necessarily set nav/keyboard ref? + const ImGuiItemFlags item_flags_backup = g.CurrentItemFlags; + g.CurrentItemFlags |= ImGuiItemFlags_NoNavDefaultFocus; window->DC.NavLayerCurrent = ImGuiNavLayer_Menu; // Layout buttons @@ -5484,16 +5851,15 @@ void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& titl *p_open = false; window->DC.NavLayerCurrent = ImGuiNavLayer_Main; - window->DC.ItemFlags = item_flags_backup; + g.CurrentItemFlags = item_flags_backup; // Title bar text (with: horizontal alignment, avoiding collapse/close button, optional "unsaved document" marker) // FIXME: Refactor text alignment facilities along with RenderText helpers, this is WAY too much messy code.. - const char* UNSAVED_DOCUMENT_MARKER = "*"; - const float marker_size_x = (flags & ImGuiWindowFlags_UnsavedDocument) ? CalcTextSize(UNSAVED_DOCUMENT_MARKER, NULL, false).x : 0.0f; + const float marker_size_x = (flags & ImGuiWindowFlags_UnsavedDocument) ? button_sz * 0.80f : 0.0f; const ImVec2 text_size = CalcTextSize(name, NULL, true) + ImVec2(marker_size_x, 0.0f); // As a nice touch we try to ensure that centered title text doesn't get affected by visibility of Close/Collapse button, - // while uncentered title text will still reach edges correct. + // while uncentered title text will still reach edges correctly. if (pad_l > style.FramePadding.x) pad_l += g.Style.ItemInnerSpacing.x; if (pad_r > style.FramePadding.x) @@ -5507,23 +5873,31 @@ void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& titl } ImRect layout_r(title_bar_rect.Min.x + pad_l, title_bar_rect.Min.y, title_bar_rect.Max.x - pad_r, title_bar_rect.Max.y); - ImRect clip_r(layout_r.Min.x, layout_r.Min.y, layout_r.Max.x + g.Style.ItemInnerSpacing.x, layout_r.Max.y); - //if (g.IO.KeyCtrl) window->DrawList->AddRect(layout_r.Min, layout_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG] - RenderTextClipped(layout_r.Min, layout_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_r); + ImRect clip_r(layout_r.Min.x, layout_r.Min.y, ImMin(layout_r.Max.x + g.Style.ItemInnerSpacing.x, title_bar_rect.Max.x), layout_r.Max.y); if (flags & ImGuiWindowFlags_UnsavedDocument) { - ImVec2 marker_pos = ImVec2(ImMax(layout_r.Min.x, layout_r.Min.x + (layout_r.GetWidth() - text_size.x) * style.WindowTitleAlign.x) + text_size.x, layout_r.Min.y) + ImVec2(2 - marker_size_x, 0.0f); - ImVec2 off = ImVec2(0.0f, IM_FLOOR(-g.FontSize * 0.25f)); - RenderTextClipped(marker_pos + off, layout_r.Max + off, UNSAVED_DOCUMENT_MARKER, NULL, NULL, ImVec2(0, style.WindowTitleAlign.y), &clip_r); + ImVec2 marker_pos; + marker_pos.x = ImClamp(layout_r.Min.x + (layout_r.GetWidth() - text_size.x) * style.WindowTitleAlign.x + text_size.x, layout_r.Min.x, layout_r.Max.x); + marker_pos.y = (layout_r.Min.y + layout_r.Max.y) * 0.5f; + if (marker_pos.x > layout_r.Min.x) + { + RenderBullet(window->DrawList, marker_pos, GetColorU32(ImGuiCol_Text)); + clip_r.Max.x = ImMin(clip_r.Max.x, marker_pos.x - (int)(marker_size_x * 0.5f)); + } } + //if (g.IO.KeyShift) window->DrawList->AddRect(layout_r.Min, layout_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG] + //if (g.IO.KeyCtrl) window->DrawList->AddRect(clip_r.Min, clip_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG] + RenderTextClipped(layout_r.Min, layout_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_r); } void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window) { window->ParentWindow = parent_window; - window->RootWindow = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window; + window->RootWindow = window->RootWindowPopupTree = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window; if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip)) window->RootWindow = parent_window->RootWindow; + if (parent_window && (flags & ImGuiWindowFlags_Popup)) + window->RootWindowPopupTree = parent_window->RootWindowPopupTree; if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup))) window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight; while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened) @@ -5533,6 +5907,38 @@ void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags } } +// When a modal popup is open, newly created windows that want focus (i.e. are not popups and do not specify ImGuiWindowFlags_NoFocusOnAppearing) +// should be positioned behind that modal window, unless the window was created inside the modal begin-stack. +// In case of multiple stacked modals newly created window honors begin stack order and does not go below its own modal parent. +// - Window // FindBlockingModal() returns Modal1 +// - Window // .. returns Modal1 +// - Modal1 // .. returns Modal2 +// - Window // .. returns Modal2 +// - Window // .. returns Modal2 +// - Modal2 // .. returns Modal2 +static ImGuiWindow* ImGui::FindBlockingModal(ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + if (g.OpenPopupStack.Size <= 0) + return NULL; + + // Find a modal that has common parent with specified window. Specified window should be positioned behind that modal. + for (int i = g.OpenPopupStack.Size - 1; i >= 0; i--) + { + ImGuiWindow* popup_window = g.OpenPopupStack.Data[i].Window; + if (popup_window == NULL || !(popup_window->Flags & ImGuiWindowFlags_Modal)) + continue; + if (!popup_window->Active && !popup_window->WasActive) // Check WasActive, because this code may run before popup renders on current frame, also check Active to handle newly created windows. + continue; + if (IsWindowWithinBeginStackOf(window, popup_window)) // Window is rendered over last modal, no render order change needed. + break; + for (ImGuiWindow* parent = popup_window->ParentWindowInBeginStack->RootWindow; parent != NULL; parent = parent->ParentWindowInBeginStack->RootWindow) + if (IsWindowWithinBeginStackOf(window, parent)) + return popup_window; // Place window above its begin stack parent. + } + return NULL; +} + // Push a new Dear ImGui window to add widgets to. // - A default window called "Debug" is automatically stacked at the beginning of every frame so you can use widgets without explicitly calling a Begin/End pair. // - Begin/End can be called multiple times during the frame with the same window name to append content. @@ -5553,6 +5959,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) const bool window_just_created = (window == NULL); if (window_just_created) window = CreateNewWindow(name, flags); + else + UpdateWindowInFocusOrderList(window, window_just_created, flags); // Automatically disable manual moving/resizing when NoInputs is set if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs) @@ -5567,14 +5975,13 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Update the Appearing flag bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on - const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesCannotSkipItems > 0); if (flags & ImGuiWindowFlags_Popup) { ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size]; window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed window_just_activated_by_user |= (window != popup_ref.Window); } - window->Appearing = (window_just_activated_by_user || window_just_appearing_after_hidden_for_resize); + window->Appearing = window_just_activated_by_user; if (window->Appearing) SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true); @@ -5593,7 +6000,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) } // Parent window is latched only on the first call to Begin() of the frame, so further append-calls can be done from a different window stack - ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back(); + ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back().Window; ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow; IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow)); @@ -5603,10 +6010,15 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Add to stack // We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow() - g.CurrentWindowStack.push_back(window); g.CurrentWindow = window; - window->DC.StackSizesOnBegin.SetToCurrentState(); + ImGuiWindowStackData window_stack_data; + window_stack_data.Window = window; + window_stack_data.ParentLastItemDataBackup = g.LastItemData; + window_stack_data.StackSizesOnBegin.SetToCurrentState(); + g.CurrentWindowStack.push_back(window_stack_data); g.CurrentWindow = NULL; + if (flags & ImGuiWindowFlags_ChildMenu) + g.BeginMenuCount++; if (flags & ImGuiWindowFlags_Popup) { @@ -5616,12 +6028,12 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->PopupId = popup_ref.PopupId; } - if (window_just_appearing_after_hidden_for_resize && !(flags & ImGuiWindowFlags_ChildWindow)) - window->NavLastIds[0] = 0; - // Update ->RootWindow and others pointers (before any possible call to FocusWindow) if (first_begin_of_the_frame) + { UpdateWindowParentAndRootLinks(window, flags, parent_window); + window->ParentWindowInBeginStack = parent_window_in_stack; + } // Process SetNextWindow***() calls // (FIXME: Consider splitting the HasXXX flags into X/Y components @@ -5678,6 +6090,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { // Initialize const bool window_is_child_tooltip = (flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_Tooltip); // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345) + const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesCannotSkipItems > 0); window->Active = true; window->HasCloseButton = (p_open != NULL); window->ClipRect = ImVec4(-FLT_MAX, -FLT_MAX, +FLT_MAX, +FLT_MAX); @@ -5755,13 +6168,12 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { // We don't use a regular button+id to test for double-click on title bar (mostly due to legacy reason, could be fixed), so verify that we don't have items over the title bar. ImRect title_bar_rect = window->TitleBarRect(); - if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseDoubleClicked[0]) + if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseClickedCount[0] == 2) window->WantCollapseToggle = true; if (window->WantCollapseToggle) { window->Collapsed = !window->Collapsed; MarkIniSettingsDirty(window); - FocusWindow(window); } } else @@ -5847,9 +6259,11 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Calculate the range of allowed position for that window (to be movable and visible past safe area padding) // When clamping to stay visible, we will enforce that window->Pos stays inside of visibility_rect. - ImRect viewport_rect(GetViewportRect()); + ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)GetMainViewport(); + ImRect viewport_rect(viewport->GetMainRect()); + ImRect viewport_work_rect(viewport->GetWorkRect()); ImVec2 visibility_padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding); - ImRect visibility_rect(viewport_rect.Min + visibility_padding, viewport_rect.Max - visibility_padding); + ImRect visibility_rect(viewport_work_rect.Min + visibility_padding, viewport_work_rect.Max - visibility_padding); // Clamp position/size so window stays visible within its viewport or monitor // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing. @@ -5874,8 +6288,36 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) want_focus = true; else if ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) == 0) want_focus = true; + + ImGuiWindow* modal = GetTopMostPopupModal(); + if (modal != NULL && !IsWindowWithinBeginStackOf(window, modal)) + { + // Avoid focusing a window that is created outside of active modal. This will prevent active modal from being closed. + // Since window is not focused it would reappear at the same display position like the last time it was visible. + // In case of completely new windows it would go to the top (over current modal), but input to such window would still be blocked by modal. + // Position window behind a modal that is not a begin-parent of this window. + want_focus = false; + if (window == window->RootWindow) + { + ImGuiWindow* blocking_modal = FindBlockingModal(window); + IM_ASSERT(blocking_modal != NULL); + BringWindowToDisplayBehind(window, blocking_modal); + } + } } + // [Test Engine] Register whole window in the item system +#ifdef IMGUI_ENABLE_TEST_ENGINE + if (g.TestEngineHookItems) + { + IM_ASSERT(window->IDStack.Size == 1); + window->IDStack.Size = 0; + IMGUI_TEST_ENGINE_ITEM_ADD(window->Rect(), window->ID); + IMGUI_TEST_ENGINE_ITEM_INFO(window->ID, window->Name, (g.HoveredWindow == window) ? ImGuiItemStatusFlags_HoveredRect : 0); + window->IDStack.Size = 1; + } +#endif + // Handle manual resize: Resize Grips, Borders, Gamepad int border_held = -1; ImU32 resize_grip_col[4] = {}; @@ -5924,7 +6366,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Inner rectangle // Not affected by window border size. Used by: // - InnerClipRect - // - ScrollToBringRectIntoView() + // - ScrollToRectEx() // - NavUpdatePageUpPageDown() // - Scrollbar() window->InnerRect.Min.x = window->Pos.x; @@ -5971,34 +6413,21 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID); PushClipRect(host_rect.Min, host_rect.Max, false); - // Draw modal window background (darkens what is behind them, all viewports) - const bool dim_bg_for_modal = (flags & ImGuiWindowFlags_Modal) && window == GetTopMostPopupModal() && window->HiddenFramesCannotSkipItems <= 0; - const bool dim_bg_for_window_list = g.NavWindowingTargetAnim && (window == g.NavWindowingTargetAnim->RootWindow); - if (dim_bg_for_modal || dim_bg_for_window_list) - { - const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio); - window->DrawList->AddRectFilled(viewport_rect.Min, viewport_rect.Max, dim_bg_col); - } - - // Draw navigation selection/windowing rectangle background - if (dim_bg_for_window_list && window == g.NavWindowingTargetAnim) - { - ImRect bb = window->Rect(); - bb.Expand(g.FontSize); - if (!bb.Contains(viewport_rect)) // Avoid drawing if the window covers all the viewport anyway - window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha * 0.25f), g.Style.WindowRounding); - } - - // Since 1.71, child window can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call. + // Child windows can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call (since 1.71) // When using overlapping child windows, this will break the assumption that child z-order is mapped to submission order. - // We disable this when the parent window has zero vertices, which is a common pattern leading to laying out multiple overlapping child. - // We also disabled this when we have dimming overlay behind this specific one child. - // FIXME: More code may rely on explicit sorting of overlapping child window and would need to disable this somehow. Please get in contact if you are affected. + // FIXME: User code may rely on explicit sorting of overlapping child window and would need to disable this somehow. Please get in contact if you are affected (github #4493) { bool render_decorations_in_parent = false; if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) - if (window->DrawList->CmdBuffer.back().ElemCount == 0 && parent_window->DrawList->VtxBuffer.Size > 0) + { + // - We test overlap with the previous child window only (testing all would end up being O(log N) not a good investment here) + // - We disable this when the parent window has zero vertices, which is a common pattern leading to laying out multiple overlapping childs + ImGuiWindow* previous_child = parent_window->DC.ChildWindows.Size >= 2 ? parent_window->DC.ChildWindows[parent_window->DC.ChildWindows.Size - 2] : NULL; + bool previous_child_overlapping = previous_child ? previous_child->Rect().Overlaps(window->Rect()) : false; + bool parent_is_empty = parent_window->DrawList->VtxBuffer.Size > 0; + if (window->DrawList->CmdBuffer.back().ElemCount == 0 && parent_is_empty && !previous_child_overlapping) render_decorations_in_parent = true; + } if (render_decorations_in_parent) window->DrawList = parent_window->DrawList; @@ -6011,20 +6440,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DrawList = &window->DrawListInst; } - // Draw navigation selection/windowing rectangle border - if (g.NavWindowingTargetAnim == window) - { - float rounding = ImMax(window->WindowRounding, g.Style.WindowRounding); - ImRect bb = window->Rect(); - bb.Expand(g.FontSize); - if (bb.Contains(viewport_rect)) // If a window fits the entire viewport, adjust its highlight inward - { - bb.Expand(-g.FontSize - 1.0f); - rounding = window->WindowRounding; - } - window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), rounding, ~0, 3.0f); - } - // UPDATE RECTANGLES (2- THOSE AFFECTED BY SCROLLING) // Work rectangle. @@ -6056,7 +6471,13 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DC.Indent.x = 0.0f + window->WindowPadding.x - window->Scroll.x; window->DC.GroupOffset.x = 0.0f; window->DC.ColumnsOffset.x = 0.0f; - window->DC.CursorStartPos = window->Pos + ImVec2(window->DC.Indent.x + window->DC.ColumnsOffset.x, decoration_up_height + window->WindowPadding.y - window->Scroll.y); + + // Record the loss of precision of CursorStartPos which can happen due to really large scrolling amount. + // This is used by clipper to compensate and fix the most common use case of large scroll area. Easy and cheap, next best thing compared to switching everything to double or ImU64. + double start_pos_highp_x = (double)window->Pos.x + window->WindowPadding.x - (double)window->Scroll.x + window->DC.ColumnsOffset.x; + double start_pos_highp_y = (double)window->Pos.y + window->WindowPadding.y - (double)window->Scroll.y + decoration_up_height; + window->DC.CursorStartPos = ImVec2((float)start_pos_highp_x, (float)start_pos_highp_y); + window->DC.CursorStartPosLossyness = ImVec2((float)(start_pos_highp_x - window->DC.CursorStartPos.x), (float)(start_pos_highp_y - window->DC.CursorStartPos.y)); window->DC.CursorPos = window->DC.CursorStartPos; window->DC.CursorPosPrevLine = window->DC.CursorPos; window->DC.CursorMaxPos = window->DC.CursorStartPos; @@ -6065,13 +6486,13 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f; window->DC.NavLayerCurrent = ImGuiNavLayer_Main; - window->DC.NavLayerActiveMask = window->DC.NavLayerActiveMaskNext; - window->DC.NavLayerActiveMaskNext = 0x00; + window->DC.NavLayersActiveMask = window->DC.NavLayersActiveMaskNext; + window->DC.NavLayersActiveMaskNext = 0x00; window->DC.NavHideHighlightOneFrame = false; window->DC.NavHasScroll = (window->ScrollMax.y > 0.0f); window->DC.MenuBarAppending = false; - window->DC.MenuColumns.Update(3, style.ItemSpacing.x, window_just_activated_by_user); + window->DC.MenuColumns.Update(style.ItemSpacing.x, window_just_activated_by_user); window->DC.TreeDepth = 0; window->DC.TreeJumpToParentOnPopMask = 0x00; window->DC.ChildWindows.resize(0); @@ -6079,7 +6500,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DC.CurrentColumns = NULL; window->DC.LayoutType = ImGuiLayoutType_Vertical; window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical; - window->DC.FocusCounterRegular = window->DC.FocusCounterTabStop = -1; window->DC.ItemWidth = window->ItemWidthDefault; window->DC.TextWrapPos = -1.0f; // disabled @@ -6095,12 +6515,12 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (want_focus) { FocusWindow(window); - NavInitWindow(window, false); + NavInitWindow(window, false); // <-- this is in the way for us to be able to defer and sort reappearing FocusWindow() calls } // Title bar if (!(flags & ImGuiWindowFlags_NoTitleBar)) - RenderWindowTitleBarContents(window, title_bar_rect, name, p_open); + RenderWindowTitleBarContents(window, ImRect(title_bar_rect.Min.x + window->WindowBorderSize, title_bar_rect.Min.y, title_bar_rect.Max.x - window->WindowBorderSize, title_bar_rect.Max.y), name, p_open); // Clear hit test shape every frame window->HitTestHoleSize.x = window->HitTestHoleSize.y = 0; @@ -6117,12 +6537,11 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // We fill last item data based on Title Bar/Tab, in order for IsItemHovered() and IsItemActive() to be usable after Begin(). // This is useful to allow creating context menus on title bar only, etc. - SetLastItemData(window, window->MoveId, IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0, title_bar_rect); + SetLastItemData(window->MoveId, g.CurrentItemFlags, IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0, title_bar_rect); -#ifdef IMGUI_ENABLE_TEST_ENGINE + // [Test Engine] Register title bar / tab if (!(window->Flags & ImGuiWindowFlags_NoTitleBar)) - IMGUI_TEST_ENGINE_ITEM_ADD(window->DC.LastItemRect, window->DC.LastItemId); -#endif + IMGUI_TEST_ENGINE_ITEM_ADD(g.LastItemData.Rect, g.LastItemData.ID); } else { @@ -6131,8 +6550,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) } // Pull/inherit current state - window->DC.ItemFlags = g.ItemFlagsStack.back(); // Inherit from shared stack - window->DC.NavFocusScopeIdCurrent = (flags & ImGuiWindowFlags_ChildWindow) ? parent_window->DC.NavFocusScopeIdCurrent : 0; // Inherit from parent only // -V595 + window->DC.NavFocusScopeIdCurrent = (flags & ImGuiWindowFlags_ChildWindow) ? parent_window->DC.NavFocusScopeIdCurrent : window->GetID("#FOCUSSCOPE"); // Inherit from parent only // -V595 PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true); @@ -6150,9 +6568,12 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar). IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0); if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) // FIXME: Doesn't make sense for ChildWindow?? - if (!g.LogEnabled) + { + const bool nav_request = (flags & ImGuiWindowFlags_NavFlattened) && (g.NavAnyRequest && g.NavWindow && g.NavWindow->RootWindowForNav == window->RootWindowForNav); + if (!g.LogEnabled && !nav_request) if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y) window->HiddenFramesCanSkipItems = 1; + } // Hide along with parent or if parent is collapsed if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCanSkipItems > 0)) @@ -6166,11 +6587,19 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->HiddenFramesCanSkipItems = 1; // Update the Hidden flag - window->Hidden = (window->HiddenFramesCanSkipItems > 0) || (window->HiddenFramesCannotSkipItems > 0) || (window->HiddenFramesForRenderOnly > 0); + bool hidden_regular = (window->HiddenFramesCanSkipItems > 0) || (window->HiddenFramesCannotSkipItems > 0); + window->Hidden = hidden_regular || (window->HiddenFramesForRenderOnly > 0); + + // Disable inputs for requested number of frames + if (window->DisableInputsFrames > 0) + { + window->DisableInputsFrames--; + window->Flags |= ImGuiWindowFlags_NoInputs; + } // Update the SkipItems flag, used to early out of all items functions (no layout required) bool skip_items = false; - if (window->Collapsed || !window->Active || window->Hidden) + if (window->Collapsed || !window->Active || hidden_regular) if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesCannotSkipItems <= 0) skip_items = true; window->SkipItems = skip_items; @@ -6206,25 +6635,35 @@ void ImGui::End() LogFinish(); // Pop from window stack - g.CurrentWindowStack.pop_back(); + g.LastItemData = g.CurrentWindowStack.back().ParentLastItemDataBackup; + if (window->Flags & ImGuiWindowFlags_ChildMenu) + g.BeginMenuCount--; if (window->Flags & ImGuiWindowFlags_Popup) g.BeginPopupStack.pop_back(); - window->DC.StackSizesOnBegin.CompareWithCurrentState(); - SetCurrentWindow(g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back()); + g.CurrentWindowStack.back().StackSizesOnBegin.CompareWithCurrentState(); + g.CurrentWindowStack.pop_back(); + SetCurrentWindow(g.CurrentWindowStack.Size == 0 ? NULL : g.CurrentWindowStack.back().Window); } void ImGui::BringWindowToFocusFront(ImGuiWindow* window) { ImGuiContext& g = *GImGui; + IM_ASSERT(window == window->RootWindow); + + const int cur_order = window->FocusOrder; + IM_ASSERT(g.WindowsFocusOrder[cur_order] == window); if (g.WindowsFocusOrder.back() == window) return; - for (int i = g.WindowsFocusOrder.Size - 2; i >= 0; i--) // We can ignore the top-most window - if (g.WindowsFocusOrder[i] == window) - { - memmove(&g.WindowsFocusOrder[i], &g.WindowsFocusOrder[i + 1], (size_t)(g.WindowsFocusOrder.Size - i - 1) * sizeof(ImGuiWindow*)); - g.WindowsFocusOrder[g.WindowsFocusOrder.Size - 1] = window; - break; - } + + const int new_order = g.WindowsFocusOrder.Size - 1; + for (int n = cur_order; n < new_order; n++) + { + g.WindowsFocusOrder[n] = g.WindowsFocusOrder[n + 1]; + g.WindowsFocusOrder[n]->FocusOrder--; + IM_ASSERT(g.WindowsFocusOrder[n]->FocusOrder == n); + } + g.WindowsFocusOrder[new_order] = window; + window->FocusOrder = (short)new_order; } void ImGui::BringWindowToDisplayFront(ImGuiWindow* window) @@ -6256,6 +6695,34 @@ void ImGui::BringWindowToDisplayBack(ImGuiWindow* window) } } +void ImGui::BringWindowToDisplayBehind(ImGuiWindow* window, ImGuiWindow* behind_window) +{ + IM_ASSERT(window != NULL && behind_window != NULL); + ImGuiContext& g = *GImGui; + window = window->RootWindow; + behind_window = behind_window->RootWindow; + int pos_wnd = FindWindowDisplayIndex(window); + int pos_beh = FindWindowDisplayIndex(behind_window); + if (pos_wnd < pos_beh) + { + size_t copy_bytes = (pos_beh - pos_wnd - 1) * sizeof(ImGuiWindow*); + memmove(&g.Windows.Data[pos_wnd], &g.Windows.Data[pos_wnd + 1], copy_bytes); + g.Windows[pos_beh - 1] = window; + } + else + { + size_t copy_bytes = (pos_wnd - pos_beh) * sizeof(ImGuiWindow*); + memmove(&g.Windows.Data[pos_beh + 1], &g.Windows.Data[pos_beh], copy_bytes); + g.Windows[pos_beh] = window; + } +} + +int ImGui::FindWindowDisplayIndex(ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + return g.Windows.index_from_ptr(g.Windows.find(window)); +} + // Moving window to front of display and set focus (which happens to be back of our sorted list) void ImGui::FocusWindow(ImGuiWindow* window) { @@ -6266,11 +6733,12 @@ void ImGui::FocusWindow(ImGuiWindow* window) g.NavWindow = window; if (window && g.NavDisableMouseHover) g.NavMousePosDirty = true; - g.NavInitRequest = false; g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId g.NavFocusScopeId = 0; g.NavIdIsAlive = false; g.NavLayer = ImGuiNavLayer_Main; + g.NavInitRequest = g.NavMoveSubmitted = g.NavMoveScoringItems = false; + NavUpdateAnyRequestFlag(); //IMGUI_DEBUG_LOG("FocusWindow(\"%s\")\n", window ? window->Name : NULL); } @@ -6302,19 +6770,24 @@ void ImGui::FocusWindow(ImGuiWindow* window) void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window) { ImGuiContext& g = *GImGui; - int start_idx = g.WindowsFocusOrder.Size - 1; if (under_this_window != NULL) { - int under_this_window_idx = FindWindowFocusIndex(under_this_window); - if (under_this_window_idx != -1) - start_idx = under_this_window_idx - 1; + // Aim at root window behind us, if we are in a child window that's our own root (see #4640) + int offset = -1; + while (under_this_window->Flags & ImGuiWindowFlags_ChildWindow) + { + under_this_window = under_this_window->ParentWindow; + offset = 0; + } + start_idx = FindWindowFocusIndex(under_this_window) + offset; } for (int i = start_idx; i >= 0; i--) { // We may later decide to test for different NoXXXInputs based on the active navigation input (mouse vs nav) but that may feel more confusing to the user. ImGuiWindow* window = g.WindowsFocusOrder[i]; - if (window != ignore_window && window->WasActive && !(window->Flags & ImGuiWindowFlags_ChildWindow)) + IM_ASSERT(window == window->RootWindow); + if (window != ignore_window && window->WasActive) if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) { ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(window); @@ -6325,6 +6798,7 @@ void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWind FocusWindow(NULL); } +// Important: this alone doesn't alter current ImDrawList state. This is called by PushFont/PopFont only. void ImGui::SetCurrentFont(ImFont* font) { ImGuiContext& g = *GImGui; @@ -6362,24 +6836,56 @@ void ImGui::PopFont() void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled) { ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - ImGuiItemFlags item_flags = window->DC.ItemFlags; + ImGuiItemFlags item_flags = g.CurrentItemFlags; IM_ASSERT(item_flags == g.ItemFlagsStack.back()); if (enabled) item_flags |= option; else item_flags &= ~option; - window->DC.ItemFlags = item_flags; + g.CurrentItemFlags = item_flags; g.ItemFlagsStack.push_back(item_flags); } void ImGui::PopItemFlag() { ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; IM_ASSERT(g.ItemFlagsStack.Size > 1); // Too many calls to PopItemFlag() - we always leave a 0 at the bottom of the stack. g.ItemFlagsStack.pop_back(); - window->DC.ItemFlags = g.ItemFlagsStack.back(); + g.CurrentItemFlags = g.ItemFlagsStack.back(); +} + +// BeginDisabled()/EndDisabled() +// - Those can be nested but it cannot be used to enable an already disabled section (a single BeginDisabled(true) in the stack is enough to keep everything disabled) +// - Visually this is currently altering alpha, but it is expected that in a future styling system this would work differently. +// - Feedback welcome at https://github.com/ocornut/imgui/issues/211 +// - BeginDisabled(false) essentially does nothing useful but is provided to facilitate use of boolean expressions. If you can avoid calling BeginDisabled(False)/EndDisabled() best to avoid it. +// - Optimized shortcuts instead of PushStyleVar() + PushItemFlag() +void ImGui::BeginDisabled(bool disabled) +{ + ImGuiContext& g = *GImGui; + bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0; + if (!was_disabled && disabled) + { + g.DisabledAlphaBackup = g.Style.Alpha; + g.Style.Alpha *= g.Style.DisabledAlpha; // PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * g.Style.DisabledAlpha); + } + if (was_disabled || disabled) + g.CurrentItemFlags |= ImGuiItemFlags_Disabled; + g.ItemFlagsStack.push_back(g.CurrentItemFlags); + g.DisabledStackSize++; +} + +void ImGui::EndDisabled() +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(g.DisabledStackSize > 0); + g.DisabledStackSize--; + bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0; + //PopItemFlag(); + g.ItemFlagsStack.pop_back(); + g.CurrentItemFlags = g.ItemFlagsStack.back(); + if (was_disabled && (g.CurrentItemFlags & ImGuiItemFlags_Disabled) == 0) + g.Style.Alpha = g.DisabledAlphaBackup; //PopStyleVar(); } // FIXME: Look into renaming this once we have settled the new Focus/Activation/TabStop system. @@ -6406,33 +6912,68 @@ void ImGui::PopButtonRepeat() void ImGui::PushTextWrapPos(float wrap_pos_x) { ImGuiWindow* window = GetCurrentWindow(); + window->DC.TextWrapPosStack.push_back(window->DC.TextWrapPos); window->DC.TextWrapPos = wrap_pos_x; - window->DC.TextWrapPosStack.push_back(wrap_pos_x); } void ImGui::PopTextWrapPos() { ImGuiWindow* window = GetCurrentWindow(); + window->DC.TextWrapPos = window->DC.TextWrapPosStack.back(); window->DC.TextWrapPosStack.pop_back(); - window->DC.TextWrapPos = window->DC.TextWrapPosStack.empty() ? -1.0f : window->DC.TextWrapPosStack.back(); } -bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent) +static ImGuiWindow* GetCombinedRootWindow(ImGuiWindow* window, bool popup_hierarchy) { - if (window->RootWindow == potential_parent) + ImGuiWindow* last_window = NULL; + while (last_window != window) + { + last_window = window; + window = window->RootWindow; + if (popup_hierarchy) + window = window->RootWindowPopupTree; + } + return window; +} + +bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool popup_hierarchy) +{ + ImGuiWindow* window_root = GetCombinedRootWindow(window, popup_hierarchy); + if (window_root == potential_parent) return true; while (window != NULL) { if (window == potential_parent) return true; + if (window == window_root) // end of chain + return false; window = window->ParentWindow; } return false; } +bool ImGui::IsWindowWithinBeginStackOf(ImGuiWindow* window, ImGuiWindow* potential_parent) +{ + if (window->RootWindow == potential_parent) + return true; + while (window != NULL) + { + if (window == potential_parent) + return true; + window = window->ParentWindowInBeginStack; + } + return false; +} + bool ImGui::IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below) { ImGuiContext& g = *GImGui; + + // It would be saner to ensure that display layer is always reflected in the g.Windows[] order, which would likely requires altering all manipulations of that array + const int display_layer_delta = GetWindowDisplayLayer(potential_above) - GetWindowDisplayLayer(potential_below); + if (display_layer_delta != 0) + return display_layer_delta > 0; + for (int i = g.Windows.Size - 1; i >= 0; i--) { ImGuiWindow* candidate_window = g.Windows[i]; @@ -6446,41 +6987,33 @@ bool ImGui::IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_b bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags) { - IM_ASSERT((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0); // Flags not supported by this function + IM_ASSERT((flags & (ImGuiHoveredFlags_AllowWhenOverlapped | ImGuiHoveredFlags_AllowWhenDisabled)) == 0); // Flags not supported by this function ImGuiContext& g = *GImGui; + ImGuiWindow* ref_window = g.HoveredWindow; + ImGuiWindow* cur_window = g.CurrentWindow; + if (ref_window == NULL) + return false; - if (flags & ImGuiHoveredFlags_AnyWindow) + if ((flags & ImGuiHoveredFlags_AnyWindow) == 0) { - if (g.HoveredWindow == NULL) + IM_ASSERT(cur_window); // Not inside a Begin()/End() + const bool popup_hierarchy = (flags & ImGuiHoveredFlags_NoPopupHierarchy) == 0; + if (flags & ImGuiHoveredFlags_RootWindow) + cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy); + + bool result; + if (flags & ImGuiHoveredFlags_ChildWindows) + result = IsWindowChildOf(ref_window, cur_window, popup_hierarchy); + else + result = (ref_window == cur_window); + if (!result) return false; } - else - { - switch (flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) - { - case ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows: - if (g.HoveredRootWindow != g.CurrentWindow->RootWindow) - return false; - break; - case ImGuiHoveredFlags_RootWindow: - if (g.HoveredWindow != g.CurrentWindow->RootWindow) - return false; - break; - case ImGuiHoveredFlags_ChildWindows: - if (g.HoveredWindow == NULL || !IsWindowChildOf(g.HoveredWindow, g.CurrentWindow)) - return false; - break; - default: - if (g.HoveredWindow != g.CurrentWindow) - return false; - break; - } - } - if (!IsWindowContentHoverable(g.HoveredWindow, flags)) + if (!IsWindowContentHoverable(ref_window, flags)) return false; if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) - if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != g.HoveredWindow->MoveId) + if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != ref_window->MoveId) return false; return true; } @@ -6488,22 +7021,23 @@ bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags) bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags) { ImGuiContext& g = *GImGui; + ImGuiWindow* ref_window = g.NavWindow; + ImGuiWindow* cur_window = g.CurrentWindow; + if (ref_window == NULL) + return false; if (flags & ImGuiFocusedFlags_AnyWindow) - return g.NavWindow != NULL; + return true; - IM_ASSERT(g.CurrentWindow); // Not inside a Begin()/End() - switch (flags & (ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows)) - { - case ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows: - return g.NavWindow && g.NavWindow->RootWindow == g.CurrentWindow->RootWindow; - case ImGuiFocusedFlags_RootWindow: - return g.NavWindow == g.CurrentWindow->RootWindow; - case ImGuiFocusedFlags_ChildWindows: - return g.NavWindow && IsWindowChildOf(g.NavWindow, g.CurrentWindow); - default: - return g.NavWindow == g.CurrentWindow; - } + IM_ASSERT(cur_window); // Not inside a Begin()/End() + const bool popup_hierarchy = (flags & ImGuiFocusedFlags_NoPopupHierarchy) == 0; + if (flags & ImGuiHoveredFlags_RootWindow) + cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy); + + if (flags & ImGuiHoveredFlags_ChildWindows) + return IsWindowChildOf(ref_window, cur_window, popup_hierarchy); + else + return (ref_window == cur_window); } // Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext) @@ -6549,6 +7083,7 @@ void ImGui::SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond) ImVec2 offset = window->Pos - old_pos; window->DC.CursorPos += offset; // As we happen to move the window while it is being appended to (which is a bad idea - will smear) let's at least offset the cursor window->DC.CursorMaxPos += offset; // And more importantly we need to offset CursorMaxPos/CursorStartPos this so ContentSize calculation doesn't get affected. + window->DC.IdealMaxPos += offset; window->DC.CursorStartPos += offset; } @@ -6772,6 +7307,7 @@ void ImGui::ActivateItem(ImGuiID id) { ImGuiContext& g = *GImGui; g.NavNextActivateId = id; + g.NavNextActivateFlags = ImGuiActivateFlags_None; } void ImGui::PushFocusScope(ImGuiID id) @@ -6793,12 +7329,21 @@ void ImGui::PopFocusScope() void ImGui::SetKeyboardFocusHere(int offset) { - IM_ASSERT(offset >= -1); // -1 is allowed but not below ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - g.FocusRequestNextWindow = window; - g.FocusRequestNextCounterRegular = window->DC.FocusCounterRegular + 1 + offset; - g.FocusRequestNextCounterTabStop = INT_MAX; + IM_ASSERT(offset >= -1); // -1 is allowed but not below + g.NavWindow = window; + ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY; + NavMoveRequestSubmit(ImGuiDir_None, offset < 0 ? ImGuiDir_Up : ImGuiDir_Down, ImGuiNavMoveFlags_Tabbing | ImGuiNavMoveFlags_FocusApi, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable. + if (offset == -1) + { + NavMoveRequestResolveWithLastItem(&g.NavMoveResultLocal); + } + else + { + g.NavTabbingDir = 1; + g.NavTabbingCounter = offset + 1; + } } void ImGui::SetItemDefaultFocus() @@ -6807,15 +7352,17 @@ void ImGui::SetItemDefaultFocus() ImGuiWindow* window = g.CurrentWindow; if (!window->Appearing) return; - if (g.NavWindow == window->RootWindowForNav && (g.NavInitRequest || g.NavInitResultId != 0) && g.NavLayer == g.NavWindow->DC.NavLayerCurrent) - { - g.NavInitRequest = false; - g.NavInitResultId = g.NavWindow->DC.LastItemId; - g.NavInitResultRectRel = ImRect(g.NavWindow->DC.LastItemRect.Min - g.NavWindow->Pos, g.NavWindow->DC.LastItemRect.Max - g.NavWindow->Pos); - NavUpdateAnyRequestFlag(); - if (!IsItemVisible()) - SetScrollHereY(); - } + if (g.NavWindow != window->RootWindowForNav || (!g.NavInitRequest && g.NavInitResultId == 0) || g.NavLayer != window->DC.NavLayerCurrent) + return; + + g.NavInitRequest = false; + g.NavInitResultId = g.LastItemData.ID; + g.NavInitResultRectRel = WindowRectAbsToRel(window, g.LastItemData.Rect); + NavUpdateAnyRequestFlag(); + + // Scroll could be done in NavInitRequestApplyResult() via a opt-in flag (we however don't want regular init requests to scroll) + if (!IsItemVisible()) + ScrollToRectEx(window, g.LastItemData.Rect, ImGuiScrollFlags_None); } void ImGui::SetStateStorage(ImGuiStorage* tree) @@ -6867,6 +7414,8 @@ void ImGui::PushOverrideID(ImGuiID id) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; + if (g.DebugHookIdInfo == id) + DebugHookIdInfo(id, ImGuiDataType_ID, NULL, NULL); window->IDStack.push_back(id); } @@ -6876,11 +7425,10 @@ void ImGui::PushOverrideID(ImGuiID id) ImGuiID ImGui::GetIDWithSeed(const char* str, const char* str_end, ImGuiID seed) { ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed); - ImGui::KeepAliveID(id); -#ifdef IMGUI_ENABLE_TEST_ENGINE + KeepAliveID(id); ImGuiContext& g = *GImGui; - IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end); -#endif + if (g.DebugHookIdInfo == id) + DebugHookIdInfo(id, ImGuiDataType_String, str, str_end); return id; } @@ -6923,158 +7471,545 @@ bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max) //----------------------------------------------------------------------------- -// [SECTION] ERROR CHECKING +// [SECTION] INPUTS //----------------------------------------------------------------------------- -// Helper function to verify ABI compatibility between caller code and compiled version of Dear ImGui. -// Verify that the type sizes are matching between the calling file's compilation unit and imgui.cpp's compilation unit -// If the user has inconsistent compilation settings, imgui configuration #define, packing pragma, etc. your user code -// may see different structures than what imgui.cpp sees, which is problematic. -// We usually require settings to be in imconfig.h to make sure that they are accessible to all compilation units involved with Dear ImGui. -bool ImGui::DebugCheckVersionAndDataLayout(const char* version, size_t sz_io, size_t sz_style, size_t sz_vec2, size_t sz_vec4, size_t sz_vert, size_t sz_idx) +// Test if mouse cursor is hovering given rectangle +// NB- Rectangle is clipped by our current clip setting +// NB- Expand the rectangle to be generous on imprecise inputs systems (g.Style.TouchExtraPadding) +bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip) { - bool error = false; - if (strcmp(version, IMGUI_VERSION) != 0) { error = true; IM_ASSERT(strcmp(version, IMGUI_VERSION) == 0 && "Mismatched version string!"); } - if (sz_io != sizeof(ImGuiIO)) { error = true; IM_ASSERT(sz_io == sizeof(ImGuiIO) && "Mismatched struct layout!"); } - if (sz_style != sizeof(ImGuiStyle)) { error = true; IM_ASSERT(sz_style == sizeof(ImGuiStyle) && "Mismatched struct layout!"); } - if (sz_vec2 != sizeof(ImVec2)) { error = true; IM_ASSERT(sz_vec2 == sizeof(ImVec2) && "Mismatched struct layout!"); } - if (sz_vec4 != sizeof(ImVec4)) { error = true; IM_ASSERT(sz_vec4 == sizeof(ImVec4) && "Mismatched struct layout!"); } - if (sz_vert != sizeof(ImDrawVert)) { error = true; IM_ASSERT(sz_vert == sizeof(ImDrawVert) && "Mismatched struct layout!"); } - if (sz_idx != sizeof(ImDrawIdx)) { error = true; IM_ASSERT(sz_idx == sizeof(ImDrawIdx) && "Mismatched struct layout!"); } - return !error; + ImGuiContext& g = *GImGui; + + // Clip + ImRect rect_clipped(r_min, r_max); + if (clip) + rect_clipped.ClipWith(g.CurrentWindow->ClipRect); + + // Expand for touch input + const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding); + if (!rect_for_touch.Contains(g.IO.MousePos)) + return false; + return true; } -static void ImGui::ErrorCheckNewFrameSanityChecks() +ImGuiKeyData* ImGui::GetKeyData(ImGuiKey key) { ImGuiContext& g = *GImGui; + int index; +#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO + IM_ASSERT(key >= ImGuiKey_LegacyNativeKey_BEGIN && key < ImGuiKey_NamedKey_END); + if (IsLegacyKey(key)) + index = (g.IO.KeyMap[key] != -1) ? g.IO.KeyMap[key] : key; // Remap native->imgui or imgui->native + else + index = key; +#else + IM_ASSERT(IsNamedKey(key) && "Support for user key indices was dropped in favor of ImGuiKey. Please update backend & user code."); + index = key - ImGuiKey_NamedKey_BEGIN; +#endif + return &g.IO.KeysData[index]; +} - // Check user IM_ASSERT macro - // (IF YOU GET A WARNING OR COMPILE ERROR HERE: it means you assert macro is incorrectly defined! - // If your macro uses multiple statements, it NEEDS to be surrounded by a 'do { ... } while (0)' block. - // This is a common C/C++ idiom to allow multiple statements macros to be used in control flow blocks.) - // #define IM_ASSERT(EXPR) if (SomeCode(EXPR)) SomeMoreCode(); // Wrong! - // #define IM_ASSERT(EXPR) do { if (SomeCode(EXPR)) SomeMoreCode(); } while (0) // Correct! - if (true) IM_ASSERT(1); else IM_ASSERT(0); +#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO +int ImGui::GetKeyIndex(ImGuiKey key) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(IsNamedKey(key)); + const ImGuiKeyData* key_data = GetKeyData(key); + return (int)(key_data - g.IO.KeysData); +} +#endif - // Check user data - // (We pass an error message in the assert expression to make it visible to programmers who are not using a debugger, as most assert handlers display their argument) - IM_ASSERT(g.Initialized); - IM_ASSERT((g.IO.DeltaTime > 0.0f || g.FrameCount == 0) && "Need a positive DeltaTime!"); - IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount) && "Forgot to call Render() or EndFrame() at the end of the previous frame?"); - IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f && "Invalid DisplaySize value!"); - IM_ASSERT(g.IO.Fonts->Fonts.Size > 0 && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8()?"); - IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded() && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8()?"); - IM_ASSERT(g.Style.CurveTessellationTol > 0.0f && "Invalid style setting!"); - IM_ASSERT(g.Style.CircleSegmentMaxError > 0.0f && "Invalid style setting!"); - IM_ASSERT(g.Style.Alpha >= 0.0f && g.Style.Alpha <= 1.0f && "Invalid style setting!"); // Allows us to avoid a few clamps in color computations - IM_ASSERT(g.Style.WindowMinSize.x >= 1.0f && g.Style.WindowMinSize.y >= 1.0f && "Invalid style setting."); - IM_ASSERT(g.Style.WindowMenuButtonPosition == ImGuiDir_None || g.Style.WindowMenuButtonPosition == ImGuiDir_Left || g.Style.WindowMenuButtonPosition == ImGuiDir_Right); - for (int n = 0; n < ImGuiKey_COUNT; n++) - IM_ASSERT(g.IO.KeyMap[n] >= -1 && g.IO.KeyMap[n] < IM_ARRAYSIZE(g.IO.KeysDown) && "io.KeyMap[] contains an out of bound value (need to be 0..512, or -1 for unmapped key)"); +// Those names a provided for debugging purpose and are not meant to be saved persistently not compared. +static const char* const GKeyNames[] = +{ + "Tab", "LeftArrow", "RightArrow", "UpArrow", "DownArrow", "PageUp", "PageDown", + "Home", "End", "Insert", "Delete", "Backspace", "Space", "Enter", "Escape", + "LeftCtrl", "LeftShift", "LeftAlt", "LeftSuper", "RightCtrl", "RightShift", "RightAlt", "RightSuper", "Menu", + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", + "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", + "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", + "Apostrophe", "Comma", "Minus", "Period", "Slash", "Semicolon", "Equal", "LeftBracket", + "Backslash", "RightBracket", "GraveAccent", "CapsLock", "ScrollLock", "NumLock", "PrintScreen", + "Pause", "Keypad0", "Keypad1", "Keypad2", "Keypad3", "Keypad4", "Keypad5", "Keypad6", + "Keypad7", "Keypad8", "Keypad9", "KeypadDecimal", "KeypadDivide", "KeypadMultiply", + "KeypadSubtract", "KeypadAdd", "KeypadEnter", "KeypadEqual", + "GamepadStart", "GamepadBack", "GamepadFaceUp", "GamepadFaceDown", "GamepadFaceLeft", "GamepadFaceRight", + "GamepadDpadUp", "GamepadDpadDown", "GamepadDpadLeft", "GamepadDpadRight", + "GamepadL1", "GamepadR1", "GamepadL2", "GamepadR2", "GamepadL3", "GamepadR3", + "GamepadLStickUp", "GamepadLStickDown", "GamepadLStickLeft", "GamepadLStickRight", + "GamepadRStickUp", "GamepadRStickDown", "GamepadRStickLeft", "GamepadRStickRight", + "ModCtrl", "ModShift", "ModAlt", "ModSuper" +}; +IM_STATIC_ASSERT(ImGuiKey_NamedKey_COUNT == IM_ARRAYSIZE(GKeyNames)); - // Check: required key mapping (we intentionally do NOT check all keys to not pressure user into setting up everything, but Space is required and was only added in 1.60 WIP) - if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) - IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation."); +const char* ImGui::GetKeyName(ImGuiKey key) +{ +#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO + IM_ASSERT((IsNamedKey(key) || key == ImGuiKey_None) && "Support for user key indices was dropped in favor of ImGuiKey. Please update backend and user code."); +#else + if (IsLegacyKey(key)) + { + ImGuiIO& io = GetIO(); + if (io.KeyMap[key] == -1) + return "N/A"; + IM_ASSERT(IsNamedKey((ImGuiKey)io.KeyMap[key])); + key = (ImGuiKey)io.KeyMap[key]; + } +#endif + if (key == ImGuiKey_None) + return "None"; + if (!IsNamedKey(key)) + return "Unknown"; - // Check: the io.ConfigWindowsResizeFromEdges option requires backend to honor mouse cursor changes and set the ImGuiBackendFlags_HasMouseCursors flag accordingly. - if (g.IO.ConfigWindowsResizeFromEdges && !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseCursors)) - g.IO.ConfigWindowsResizeFromEdges = false; + return GKeyNames[key - ImGuiKey_NamedKey_BEGIN]; } -static void ImGui::ErrorCheckEndFrameSanityChecks() +// Note that Dear ImGui doesn't know the meaning/semantic of ImGuiKey from 0..511: they are legacy native keycodes. +// Consider transitioning from 'IsKeyDown(MY_ENGINE_KEY_A)' (<1.87) to IsKeyDown(ImGuiKey_A) (>= 1.87) +bool ImGui::IsKeyDown(ImGuiKey key) +{ + const ImGuiKeyData* key_data = GetKeyData(key); + return key_data->Down; +} + +// t0 = previous time (e.g.: g.Time - g.IO.DeltaTime) +// t1 = current time (e.g.: g.Time) +// An event is triggered at: +// t = 0.0f t = repeat_delay, t = repeat_delay + repeat_rate*N +int ImGui::CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate) +{ + if (t1 == 0.0f) + return 1; + if (t0 >= t1) + return 0; + if (repeat_rate <= 0.0f) + return (t0 < repeat_delay) && (t1 >= repeat_delay); + const int count_t0 = (t0 < repeat_delay) ? -1 : (int)((t0 - repeat_delay) / repeat_rate); + const int count_t1 = (t1 < repeat_delay) ? -1 : (int)((t1 - repeat_delay) / repeat_rate); + const int count = count_t1 - count_t0; + return count; +} + +int ImGui::GetKeyPressedAmount(ImGuiKey key, float repeat_delay, float repeat_rate) { ImGuiContext& g = *GImGui; + const ImGuiKeyData* key_data = GetKeyData(key); + const float t = key_data->DownDuration; + return CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, repeat_delay, repeat_rate); +} - // Verify that io.KeyXXX fields haven't been tampered with. Key mods should not be modified between NewFrame() and EndFrame() - // One possible reason leading to this assert is that your backends update inputs _AFTER_ NewFrame(). - // It is known that when some modal native windows called mid-frame takes focus away, some backends such as GLFW will - // send key release events mid-frame. This would normally trigger this assertion and lead to sheared inputs. - // We silently accommodate for this case by ignoring/ the case where all io.KeyXXX modifiers were released (aka key_mod_flags == 0), - // while still correctly asserting on mid-frame key press events. - const ImGuiKeyModFlags key_mod_flags = GetMergedKeyModFlags(); - IM_ASSERT((key_mod_flags == 0 || g.IO.KeyMods == key_mod_flags) && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods"); - IM_UNUSED(key_mod_flags); +bool ImGui::IsKeyPressed(ImGuiKey key, bool repeat) +{ + ImGuiContext& g = *GImGui; + const ImGuiKeyData* key_data = GetKeyData(key); + const float t = key_data->DownDuration; + if (t == 0.0f) + return true; + if (repeat && t > g.IO.KeyRepeatDelay) + return GetKeyPressedAmount(key, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0; + return false; +} - // Recover from errors - //ErrorCheckEndFrameRecover(); +bool ImGui::IsKeyReleased(ImGuiKey key) +{ + const ImGuiKeyData* key_data = GetKeyData(key); + return key_data->DownDurationPrev >= 0.0f && !key_data->Down; +} - // Report when there is a mismatch of Begin/BeginChild vs End/EndChild calls. Important: Remember that the Begin/BeginChild API requires you - // to always call End/EndChild even if Begin/BeginChild returns false! (this is unfortunately inconsistent with most other Begin* API). - if (g.CurrentWindowStack.Size != 1) +bool ImGui::IsMouseDown(ImGuiMouseButton button) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + return g.IO.MouseDown[button]; +} + +bool ImGui::IsMouseClicked(ImGuiMouseButton button, bool repeat) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + const float t = g.IO.MouseDownDuration[button]; + if (t == 0.0f) + return true; + + if (repeat && t > g.IO.KeyRepeatDelay) { - if (g.CurrentWindowStack.Size > 1) - { - IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you forget to call End/EndChild?"); - while (g.CurrentWindowStack.Size > 1) - End(); - } - else - { - IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you call End/EndChild too much?"); - } + // FIXME: 2019/05/03: Our old repeat code was wrong here and led to doubling the repeat rate, which made it an ok rate for repeat on mouse hold. + int amount = CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate * 0.50f); + if (amount > 0) + return true; } - - IM_ASSERT_USER_ERROR(g.GroupStack.Size == 0, "Missing EndGroup call!"); + return false; } -// Experimental recovery from incorrect usage of BeginXXX/EndXXX/PushXXX/PopXXX calls. -// Must be called during or before EndFrame(). -// This is generally flawed as we are not necessarily End/Popping things in the right order. -// FIXME: Can't recover from inside BeginTabItem/EndTabItem yet. -// FIXME: Can't recover from interleaved BeginTabBar/Begin -void ImGui::ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, void* user_data) +bool ImGui::IsMouseReleased(ImGuiMouseButton button) { - // PVS-Studio V1044 is "Loop break conditions do not depend on the number of iterations" ImGuiContext& g = *GImGui; - while (g.CurrentWindowStack.Size > 0) - { -#ifdef IMGUI_HAS_TABLE - while (g.CurrentTable && (g.CurrentTable->OuterWindow == g.CurrentWindow || g.CurrentTable->InnerWindow == g.CurrentWindow)) + IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + return g.IO.MouseReleased[button]; +} + +bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + return g.IO.MouseClickedCount[button] == 2; +} + +int ImGui::GetMouseClickedCount(ImGuiMouseButton button) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + return g.IO.MouseClickedCount[button]; +} + +// Return if a mouse click/drag went past the given threshold. Valid to call during the MouseReleased frame. +// [Internal] This doesn't test if the button is pressed +bool ImGui::IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + if (lock_threshold < 0.0f) + lock_threshold = g.IO.MouseDragThreshold; + return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold; +} + +bool ImGui::IsMouseDragging(ImGuiMouseButton button, float lock_threshold) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + if (!g.IO.MouseDown[button]) + return false; + return IsMouseDragPastThreshold(button, lock_threshold); +} + +ImVec2 ImGui::GetMousePos() +{ + ImGuiContext& g = *GImGui; + return g.IO.MousePos; +} + +// NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed! +ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup() +{ + ImGuiContext& g = *GImGui; + if (g.BeginPopupStack.Size > 0) + return g.OpenPopupStack[g.BeginPopupStack.Size - 1].OpenMousePos; + return g.IO.MousePos; +} + +// We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position. +bool ImGui::IsMousePosValid(const ImVec2* mouse_pos) +{ + // The assert is only to silence a false-positive in XCode Static Analysis. + // Because GImGui is not dereferenced in every code path, the static analyzer assume that it may be NULL (which it doesn't for other functions). + IM_ASSERT(GImGui != NULL); + const float MOUSE_INVALID = -256000.0f; + ImVec2 p = mouse_pos ? *mouse_pos : GImGui->IO.MousePos; + return p.x >= MOUSE_INVALID && p.y >= MOUSE_INVALID; +} + +// [WILL OBSOLETE] This was designed for backends, but prefer having backend maintain a mask of held mouse buttons, because upcoming input queue system will make this invalid. +bool ImGui::IsAnyMouseDown() +{ + ImGuiContext& g = *GImGui; + for (int n = 0; n < IM_ARRAYSIZE(g.IO.MouseDown); n++) + if (g.IO.MouseDown[n]) + return true; + return false; +} + +// Return the delta from the initial clicking position while the mouse button is clicked or was just released. +// This is locked and return 0.0f until the mouse moves past a distance threshold at least once. +// NB: This is only valid if IsMousePosValid(). backends in theory should always keep mouse position valid when dragging even outside the client window. +ImVec2 ImGui::GetMouseDragDelta(ImGuiMouseButton button, float lock_threshold) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + if (lock_threshold < 0.0f) + lock_threshold = g.IO.MouseDragThreshold; + if (g.IO.MouseDown[button] || g.IO.MouseReleased[button]) + if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold) + if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MouseClickedPos[button])) + return g.IO.MousePos - g.IO.MouseClickedPos[button]; + return ImVec2(0.0f, 0.0f); +} + +void ImGui::ResetMouseDragDelta(ImGuiMouseButton button) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr + g.IO.MouseClickedPos[button] = g.IO.MousePos; +} + +ImGuiMouseCursor ImGui::GetMouseCursor() +{ + ImGuiContext& g = *GImGui; + return g.MouseCursor; +} + +void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type) +{ + ImGuiContext& g = *GImGui; + g.MouseCursor = cursor_type; +} + +void ImGui::CaptureKeyboardFromApp(bool capture) +{ + ImGuiContext& g = *GImGui; + g.WantCaptureKeyboardNextFrame = capture ? 1 : 0; +} + +void ImGui::CaptureMouseFromApp(bool capture) +{ + ImGuiContext& g = *GImGui; + g.WantCaptureMouseNextFrame = capture ? 1 : 0; +} + +static const char* GetInputSourceName(ImGuiInputSource source) +{ + const char* input_source_names[] = { "None", "Mouse", "Keyboard", "Gamepad", "Nav", "Clipboard" }; + IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT && source >= 0 && source < ImGuiInputSource_COUNT); + return input_source_names[source]; +} + + +// Process input queue +// - trickle_fast_inputs = false : process all events, turn into flattened input state (e.g. successive down/up/down/up will be lost) +// - trickle_fast_inputs = true : process as many events as possible (successive down/up/down/up will be trickled over several frames so nothing is lost) (new feature in 1.87) +void ImGui::UpdateInputEvents(bool trickle_fast_inputs) +{ + ImGuiContext& g = *GImGui; + ImGuiIO& io = g.IO; + + bool mouse_moved = false, mouse_wheeled = false, key_changed = false, text_inputed = false; + int mouse_button_changed = 0x00; + ImBitArray key_changed_mask; + + int event_n = 0; + for (; event_n < g.InputEventsQueue.Size; event_n++) + { + const ImGuiInputEvent* e = &g.InputEventsQueue[event_n]; + if (e->Type == ImGuiInputEventType_MousePos) { - if (log_callback) log_callback(user_data, "Recovered from missing EndTable() in '%s'", g.CurrentTable->OuterWindow->Name); - EndTable(); + ImVec2 event_pos(e->MousePos.PosX, e->MousePos.PosY); + if (IsMousePosValid(&event_pos)) + event_pos = ImVec2(ImFloorSigned(event_pos.x), ImFloorSigned(event_pos.y)); // Apply same flooring as UpdateMouseInputs() + if (io.MousePos.x != event_pos.x || io.MousePos.y != event_pos.y) + { + // Trickling Rule: Stop processing queued events if we already handled a mouse button change + if (trickle_fast_inputs && (mouse_button_changed != 0 || mouse_wheeled || key_changed || text_inputed)) + break; + io.MousePos = event_pos; + mouse_moved = true; + } } -#endif - ImGuiWindow* window = g.CurrentWindow; - IM_ASSERT(window != NULL); - while (g.CurrentTabBar != NULL) //-V1044 + else if (e->Type == ImGuiInputEventType_MouseButton) { - if (log_callback) log_callback(user_data, "Recovered from missing EndTabBar() in '%s'", window->Name); - EndTabBar(); + const ImGuiMouseButton button = e->MouseButton.Button; + IM_ASSERT(button >= 0 && button < ImGuiMouseButton_COUNT); + if (io.MouseDown[button] != e->MouseButton.Down) + { + // Trickling Rule: Stop processing queued events if we got multiple action on the same button + if (trickle_fast_inputs && ((mouse_button_changed & (1 << button)) || mouse_wheeled)) + break; + io.MouseDown[button] = e->MouseButton.Down; + mouse_button_changed |= (1 << button); + } } - while (window->DC.TreeDepth > 0) + else if (e->Type == ImGuiInputEventType_MouseWheel) { - if (log_callback) log_callback(user_data, "Recovered from missing TreePop() in '%s'", window->Name); - TreePop(); + if (e->MouseWheel.WheelX != 0.0f || e->MouseWheel.WheelY != 0.0f) + { + // Trickling Rule: Stop processing queued events if we got multiple action on the event + if (trickle_fast_inputs && (mouse_wheeled || mouse_button_changed != 0)) + break; + io.MouseWheelH += e->MouseWheel.WheelX; + io.MouseWheel += e->MouseWheel.WheelY; + mouse_wheeled = true; + } } - while (g.GroupStack.Size > window->DC.StackSizesOnBegin.SizeOfGroupStack) + else if (e->Type == ImGuiInputEventType_Key) { - if (log_callback) log_callback(user_data, "Recovered from missing EndGroup() in '%s'", window->Name); - EndGroup(); + ImGuiKey key = e->Key.Key; + IM_ASSERT(key != ImGuiKey_None); + const int keydata_index = (key - ImGuiKey_KeysData_OFFSET); + ImGuiKeyData* keydata = &io.KeysData[keydata_index]; + if (keydata->Down != e->Key.Down || keydata->AnalogValue != e->Key.AnalogValue) + { + // Trickling Rule: Stop processing queued events if we got multiple action on the same button + if (trickle_fast_inputs && keydata->Down != e->Key.Down && (key_changed_mask.TestBit(keydata_index) || text_inputed || mouse_button_changed != 0)) + break; + keydata->Down = e->Key.Down; + keydata->AnalogValue = e->Key.AnalogValue; + key_changed = true; + key_changed_mask.SetBit(keydata_index); + + if (key == ImGuiKey_ModCtrl || key == ImGuiKey_ModShift || key == ImGuiKey_ModAlt || key == ImGuiKey_ModSuper) + { + if (key == ImGuiKey_ModCtrl) { io.KeyCtrl = keydata->Down; } + if (key == ImGuiKey_ModShift) { io.KeyShift = keydata->Down; } + if (key == ImGuiKey_ModAlt) { io.KeyAlt = keydata->Down; } + if (key == ImGuiKey_ModSuper) { io.KeySuper = keydata->Down; } + io.KeyMods = GetMergedKeyModFlags(); + } + } } - while (window->IDStack.Size > 1) + else if (e->Type == ImGuiInputEventType_Char) { - if (log_callback) log_callback(user_data, "Recovered from missing PopID() in '%s'", window->Name); - PopID(); + // Trickling Rule: Stop processing queued events if keys/mouse have been interacted with + if (trickle_fast_inputs && (key_changed || mouse_button_changed != 0 || mouse_moved || mouse_wheeled)) + break; + unsigned int c = e->Text.Char; + io.InputQueueCharacters.push_back(c <= IM_UNICODE_CODEPOINT_MAX ? (ImWchar)c : IM_UNICODE_CODEPOINT_INVALID); + text_inputed = true; } - while (g.ColorStack.Size > window->DC.StackSizesOnBegin.SizeOfColorStack) + else if (e->Type == ImGuiInputEventType_Focus) { - if (log_callback) log_callback(user_data, "Recovered from missing PopStyleColor() in '%s' for ImGuiCol_%s", window->Name, GetStyleColorName(g.ColorStack.back().Col)); - PopStyleColor(); + // We intentionally overwrite this and process lower, in order to give a chance + // to multi-viewports backends to queue AddFocusEvent(false) + AddFocusEvent(true) in same frame. + io.AppFocusLost = !e->AppFocused.Focused; } - while (g.StyleVarStack.Size > window->DC.StackSizesOnBegin.SizeOfStyleVarStack) + else + { + IM_ASSERT(0 && "Unknown event!"); + } + } + + // Record trail (for domain-specific applications wanting to access a precise trail) + //if (event_n != 0) IMGUI_DEBUG_LOG("Processed: %d / Remaining: %d\n", event_n, g.InputEventsQueue.Size - event_n); + for (int n = 0; n < event_n; n++) + g.InputEventsTrail.push_back(g.InputEventsQueue[n]); + + // Remaining events will be processed on the next frame + if (event_n == g.InputEventsQueue.Size) + g.InputEventsQueue.resize(0); + else + g.InputEventsQueue.erase(g.InputEventsQueue.Data, g.InputEventsQueue.Data + event_n); + + // Clear buttons state when focus is lost + // (this is useful so e.g. releasing Alt after focus loss on Alt-Tab doesn't trigger the Alt menu toggle) + if (g.IO.AppFocusLost) + { + g.IO.ClearInputKeys(); + g.IO.AppFocusLost = false; + } +} + + +//----------------------------------------------------------------------------- +// [SECTION] ERROR CHECKING +//----------------------------------------------------------------------------- + +// Helper function to verify ABI compatibility between caller code and compiled version of Dear ImGui. +// Verify that the type sizes are matching between the calling file's compilation unit and imgui.cpp's compilation unit +// If the user has inconsistent compilation settings, imgui configuration #define, packing pragma, etc. your user code +// may see different structures than what imgui.cpp sees, which is problematic. +// We usually require settings to be in imconfig.h to make sure that they are accessible to all compilation units involved with Dear ImGui. +bool ImGui::DebugCheckVersionAndDataLayout(const char* version, size_t sz_io, size_t sz_style, size_t sz_vec2, size_t sz_vec4, size_t sz_vert, size_t sz_idx) +{ + bool error = false; + if (strcmp(version, IMGUI_VERSION) != 0) { error = true; IM_ASSERT(strcmp(version, IMGUI_VERSION) == 0 && "Mismatched version string!"); } + if (sz_io != sizeof(ImGuiIO)) { error = true; IM_ASSERT(sz_io == sizeof(ImGuiIO) && "Mismatched struct layout!"); } + if (sz_style != sizeof(ImGuiStyle)) { error = true; IM_ASSERT(sz_style == sizeof(ImGuiStyle) && "Mismatched struct layout!"); } + if (sz_vec2 != sizeof(ImVec2)) { error = true; IM_ASSERT(sz_vec2 == sizeof(ImVec2) && "Mismatched struct layout!"); } + if (sz_vec4 != sizeof(ImVec4)) { error = true; IM_ASSERT(sz_vec4 == sizeof(ImVec4) && "Mismatched struct layout!"); } + if (sz_vert != sizeof(ImDrawVert)) { error = true; IM_ASSERT(sz_vert == sizeof(ImDrawVert) && "Mismatched struct layout!"); } + if (sz_idx != sizeof(ImDrawIdx)) { error = true; IM_ASSERT(sz_idx == sizeof(ImDrawIdx) && "Mismatched struct layout!"); } + return !error; +} + +static void ImGui::ErrorCheckNewFrameSanityChecks() +{ + ImGuiContext& g = *GImGui; + + // Check user IM_ASSERT macro + // (IF YOU GET A WARNING OR COMPILE ERROR HERE: it means your assert macro is incorrectly defined! + // If your macro uses multiple statements, it NEEDS to be surrounded by a 'do { ... } while (0)' block. + // This is a common C/C++ idiom to allow multiple statements macros to be used in control flow blocks.) + // #define IM_ASSERT(EXPR) if (SomeCode(EXPR)) SomeMoreCode(); // Wrong! + // #define IM_ASSERT(EXPR) do { if (SomeCode(EXPR)) SomeMoreCode(); } while (0) // Correct! + if (true) IM_ASSERT(1); else IM_ASSERT(0); + + // Check user data + // (We pass an error message in the assert expression to make it visible to programmers who are not using a debugger, as most assert handlers display their argument) + IM_ASSERT(g.Initialized); + IM_ASSERT((g.IO.DeltaTime > 0.0f || g.FrameCount == 0) && "Need a positive DeltaTime!"); + IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount) && "Forgot to call Render() or EndFrame() at the end of the previous frame?"); + IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f && "Invalid DisplaySize value!"); + IM_ASSERT(g.IO.Fonts->IsBuilt() && "Font Atlas not built! Make sure you called ImGui_ImplXXXX_NewFrame() function for renderer backend, which should call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8()"); + IM_ASSERT(g.Style.CurveTessellationTol > 0.0f && "Invalid style setting!"); + IM_ASSERT(g.Style.CircleTessellationMaxError > 0.0f && "Invalid style setting!"); + IM_ASSERT(g.Style.Alpha >= 0.0f && g.Style.Alpha <= 1.0f && "Invalid style setting!"); // Allows us to avoid a few clamps in color computations + IM_ASSERT(g.Style.WindowMinSize.x >= 1.0f && g.Style.WindowMinSize.y >= 1.0f && "Invalid style setting."); + IM_ASSERT(g.Style.WindowMenuButtonPosition == ImGuiDir_None || g.Style.WindowMenuButtonPosition == ImGuiDir_Left || g.Style.WindowMenuButtonPosition == ImGuiDir_Right); +#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO + for (int n = ImGuiKey_NamedKey_BEGIN; n < ImGuiKey_COUNT; n++) + IM_ASSERT(g.IO.KeyMap[n] >= -1 && g.IO.KeyMap[n] < IM_ARRAYSIZE(g.IO.KeysDown) && "io.KeyMap[] contains an out of bound value (need to be 0..511, or -1 for unmapped key)"); + + // Check: required key mapping (we intentionally do NOT check all keys to not pressure user into setting up everything, but Space is required and was only added in 1.60 WIP) + if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && g.IO.BackendUsingLegacyKeyArrays == 1) + IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation."); +#endif + + // Check: the io.ConfigWindowsResizeFromEdges option requires backend to honor mouse cursor changes and set the ImGuiBackendFlags_HasMouseCursors flag accordingly. + if (g.IO.ConfigWindowsResizeFromEdges && !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseCursors)) + g.IO.ConfigWindowsResizeFromEdges = false; +} + +static void ImGui::ErrorCheckEndFrameSanityChecks() +{ + ImGuiContext& g = *GImGui; + + // Verify that io.KeyXXX fields haven't been tampered with. Key mods should not be modified between NewFrame() and EndFrame() + // One possible reason leading to this assert is that your backends update inputs _AFTER_ NewFrame(). + // It is known that when some modal native windows called mid-frame takes focus away, some backends such as GLFW will + // send key release events mid-frame. This would normally trigger this assertion and lead to sheared inputs. + // We silently accommodate for this case by ignoring/ the case where all io.KeyXXX modifiers were released (aka key_mod_flags == 0), + // while still correctly asserting on mid-frame key press events. + const ImGuiKeyModFlags key_mod_flags = GetMergedKeyModFlags(); + IM_ASSERT((key_mod_flags == 0 || g.IO.KeyMods == key_mod_flags) && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods"); + IM_UNUSED(key_mod_flags); + + // Recover from errors + //ErrorCheckEndFrameRecover(); + + // Report when there is a mismatch of Begin/BeginChild vs End/EndChild calls. Important: Remember that the Begin/BeginChild API requires you + // to always call End/EndChild even if Begin/BeginChild returns false! (this is unfortunately inconsistent with most other Begin* API). + if (g.CurrentWindowStack.Size != 1) + { + if (g.CurrentWindowStack.Size > 1) { - if (log_callback) log_callback(user_data, "Recovered from missing PopStyleVar() in '%s'", window->Name); - PopStyleVar(); + IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you forget to call End/EndChild?"); + while (g.CurrentWindowStack.Size > 1) + End(); } - while (g.FocusScopeStack.Size > window->DC.StackSizesOnBegin.SizeOfFocusScopeStack) + else { - if (log_callback) log_callback(user_data, "Recovered from missing PopFocusScope() in '%s'", window->Name); - PopFocusScope(); + IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you call End/EndChild too much?"); } + } + + IM_ASSERT_USER_ERROR(g.GroupStack.Size == 0, "Missing EndGroup call!"); +} + +// Experimental recovery from incorrect usage of BeginXXX/EndXXX/PushXXX/PopXXX calls. +// Must be called during or before EndFrame(). +// This is generally flawed as we are not necessarily End/Popping things in the right order. +// FIXME: Can't recover from inside BeginTabItem/EndTabItem yet. +// FIXME: Can't recover from interleaved BeginTabBar/Begin +void ImGui::ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, void* user_data) +{ + // PVS-Studio V1044 is "Loop break conditions do not depend on the number of iterations" + ImGuiContext& g = *GImGui; + while (g.CurrentWindowStack.Size > 0) //-V1044 + { + ErrorCheckEndWindowRecover(log_callback, user_data); + ImGuiWindow* window = g.CurrentWindow; if (g.CurrentWindowStack.Size == 1) { - IM_ASSERT(g.CurrentWindow->IsFallbackWindow); + IM_ASSERT(window->IsFallbackWindow); break; } IM_ASSERT(window == g.CurrentWindow); @@ -7091,6 +8026,66 @@ void ImGui::ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, voi } } +// Must be called before End()/EndChild() +void ImGui::ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, void* user_data) +{ + ImGuiContext& g = *GImGui; + while (g.CurrentTable && (g.CurrentTable->OuterWindow == g.CurrentWindow || g.CurrentTable->InnerWindow == g.CurrentWindow)) + { + if (log_callback) log_callback(user_data, "Recovered from missing EndTable() in '%s'", g.CurrentTable->OuterWindow->Name); + EndTable(); + } + + ImGuiWindow* window = g.CurrentWindow; + ImGuiStackSizes* stack_sizes = &g.CurrentWindowStack.back().StackSizesOnBegin; + IM_ASSERT(window != NULL); + while (g.CurrentTabBar != NULL) //-V1044 + { + if (log_callback) log_callback(user_data, "Recovered from missing EndTabBar() in '%s'", window->Name); + EndTabBar(); + } + while (window->DC.TreeDepth > 0) + { + if (log_callback) log_callback(user_data, "Recovered from missing TreePop() in '%s'", window->Name); + TreePop(); + } + while (g.GroupStack.Size > stack_sizes->SizeOfGroupStack) //-V1044 + { + if (log_callback) log_callback(user_data, "Recovered from missing EndGroup() in '%s'", window->Name); + EndGroup(); + } + while (window->IDStack.Size > 1) + { + if (log_callback) log_callback(user_data, "Recovered from missing PopID() in '%s'", window->Name); + PopID(); + } + while (g.DisabledStackSize > stack_sizes->SizeOfDisabledStack) //-V1044 + { + if (log_callback) log_callback(user_data, "Recovered from missing EndDisabled() in '%s'", window->Name); + EndDisabled(); + } + while (g.ColorStack.Size > stack_sizes->SizeOfColorStack) + { + if (log_callback) log_callback(user_data, "Recovered from missing PopStyleColor() in '%s' for ImGuiCol_%s", window->Name, GetStyleColorName(g.ColorStack.back().Col)); + PopStyleColor(); + } + while (g.ItemFlagsStack.Size > stack_sizes->SizeOfItemFlagsStack) //-V1044 + { + if (log_callback) log_callback(user_data, "Recovered from missing PopItemFlag() in '%s'", window->Name); + PopItemFlag(); + } + while (g.StyleVarStack.Size > stack_sizes->SizeOfStyleVarStack) //-V1044 + { + if (log_callback) log_callback(user_data, "Recovered from missing PopStyleVar() in '%s'", window->Name); + PopStyleVar(); + } + while (g.FocusScopeStack.Size > stack_sizes->SizeOfFocusScopeStack) //-V1044 + { + if (log_callback) log_callback(user_data, "Recovered from missing PopFocusScope() in '%s'", window->Name); + PopFocusScope(); + } +} + // Save current stack sizes for later compare void ImGuiStackSizes::SetToCurrentState() { @@ -7102,7 +8097,9 @@ void ImGuiStackSizes::SetToCurrentState() SizeOfFontStack = (short)g.FontStack.Size; SizeOfFocusScopeStack = (short)g.FocusScopeStack.Size; SizeOfGroupStack = (short)g.GroupStack.Size; + SizeOfItemFlagsStack = (short)g.ItemFlagsStack.Size; SizeOfBeginPopupStack = (short)g.BeginPopupStack.Size; + SizeOfDisabledStack = (short)g.DisabledStackSize; } // Compare to detect usage errors @@ -7120,6 +8117,8 @@ void ImGuiStackSizes::CompareWithCurrentState() // For color, style and font stacks there is an incentive to use Push/Begin/Pop/.../End patterns, so we relax our checks a little to allow them. IM_ASSERT(SizeOfGroupStack == g.GroupStack.Size && "BeginGroup/EndGroup Mismatch!"); IM_ASSERT(SizeOfBeginPopupStack == g.BeginPopupStack.Size && "BeginPopup/EndPopup or BeginMenu/EndMenu Mismatch!"); + IM_ASSERT(SizeOfDisabledStack == g.DisabledStackSize && "BeginDisabled/EndDisabled Mismatch!"); + IM_ASSERT(SizeOfItemFlagsStack >= g.ItemFlagsStack.Size && "PushItemFlag/PopItemFlag Mismatch!"); IM_ASSERT(SizeOfColorStack >= g.ColorStack.Size && "PushStyleColor/PopStyleColor Mismatch!"); IM_ASSERT(SizeOfStyleVarStack >= g.StyleVarStack.Size && "PushStyleVar/PopStyleVar Mismatch!"); IM_ASSERT(SizeOfFontStack >= g.FontStack.Size && "PushFont/PopFont Mismatch!"); @@ -7154,7 +8153,6 @@ void ImGuiStackSizes::CompareWithCurrentState() // - GetContentRegionMaxAbs() [Internal] // - GetContentRegionAvail(), // - GetWindowContentRegionMin(), GetWindowContentRegionMax() -// - GetWindowContentRegionWidth() // - BeginGroup() // - EndGroup() // Also see in imgui_widgets: tab bars, columns. @@ -7204,14 +8202,23 @@ void ImGui::ItemSize(const ImRect& bb, float text_baseline_y) // Declare item bounding box for clipping and interaction. // Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface // declare their minimum size requirement to ItemSize() and provide a larger region to ItemAdd() which is used drawing/interaction. -bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg) +bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGuiItemFlags extra_flags) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; + // Set item data + // (DisplayRect is left untouched, made valid when ImGuiItemStatusFlags_HasDisplayRect is set) + g.LastItemData.ID = id; + g.LastItemData.Rect = bb; + g.LastItemData.NavRect = nav_bb_arg ? *nav_bb_arg : bb; + g.LastItemData.InFlags = g.CurrentItemFlags | extra_flags; + g.LastItemData.StatusFlags = ImGuiItemStatusFlags_None; + + // Directional navigation processing if (id != 0) { - // Navigation processing runs prior to clipping early-out + // Runs prior to clipping early-out // (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget // (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests // unfortunately, but it is still limited to one window. It may not scale very well for windows with ten of @@ -7220,11 +8227,16 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg) // to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick). // We intentionally don't check if g.NavWindow != NULL because g.NavAnyRequest should only be set when it is non null. // If we crash on a NULL g.NavWindow we need to fix the bug elsewhere. - window->DC.NavLayerActiveMaskNext |= (1 << window->DC.NavLayerCurrent); + window->DC.NavLayersActiveMaskNext |= (1 << window->DC.NavLayerCurrent); if (g.NavId == id || g.NavAnyRequest) if (g.NavWindow->RootWindowForNav == window->RootWindowForNav) if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened)) - NavProcessItem(window, nav_bb_arg ? *nav_bb_arg : bb, id); + NavProcessItem(); + + // [DEBUG] People keep stumbling on this problem and using "" as identifier in the root of a window instead of "##something". + // Empty identifier are valid and useful in a small amount of cases, but 99.9% of the time you want to use "##something". + // READ THE FAQ: https://dearimgui.org/faq + IM_ASSERT(id != window->ID && "Cannot have an empty ID at the root of a window. If you need an empty label, use ## and read the FAQ about how the ID Stack works!"); // [DEBUG] Item Picker tool, when enabling the "extended" version we perform the check in ItemAdd() #ifdef IMGUI_DEBUG_TOOL_ITEM_PICKER_EX @@ -7235,11 +8247,6 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg) } #endif } - - // Equivalent to calling SetLastItemData() - window->DC.LastItemId = id; - window->DC.LastItemRect = bb; - window->DC.LastItemStatusFlags = ImGuiItemStatusFlags_None; g.NextItemData.Flags = ImGuiNextItemDataFlags_None; #ifdef IMGUI_ENABLE_TEST_ENGINE @@ -7248,14 +8255,14 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg) #endif // Clipping test - const bool is_clipped = IsClippedEx(bb, id, false); + const bool is_clipped = IsClippedEx(bb, id); if (is_clipped) return false; //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG] // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them) if (IsMouseHoveringRect(bb.Min, bb.Max)) - window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HoveredRect; + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredRect; return true; } @@ -7371,12 +8378,13 @@ void ImGui::SetNextItemWidth(float item_width) g.NextItemData.Width = item_width; } +// FIXME: Remove the == 0.0f behavior? void ImGui::PushItemWidth(float item_width) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; + window->DC.ItemWidthStack.push_back(window->DC.ItemWidth); // Backup current width window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width); - window->DC.ItemWidthStack.push_back(window->DC.ItemWidth); g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth; } @@ -7387,18 +8395,19 @@ void ImGui::PushMultiItemsWidths(int components, float w_full) const ImGuiStyle& style = g.Style; const float w_item_one = ImMax(1.0f, IM_FLOOR((w_full - (style.ItemInnerSpacing.x) * (components - 1)) / (float)components)); const float w_item_last = ImMax(1.0f, IM_FLOOR(w_full - (w_item_one + style.ItemInnerSpacing.x) * (components - 1))); + window->DC.ItemWidthStack.push_back(window->DC.ItemWidth); // Backup current width window->DC.ItemWidthStack.push_back(w_item_last); - for (int i = 0; i < components - 1; i++) + for (int i = 0; i < components - 2; i++) window->DC.ItemWidthStack.push_back(w_item_one); - window->DC.ItemWidth = window->DC.ItemWidthStack.back(); + window->DC.ItemWidth = (components == 1) ? w_item_last : w_item_one; g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth; } void ImGui::PopItemWidth() { ImGuiWindow* window = GetCurrentWindow(); + window->DC.ItemWidth = window->DC.ItemWidthStack.back(); window->DC.ItemWidthStack.pop_back(); - window->DC.ItemWidth = window->DC.ItemWidthStack.empty() ? window->ItemWidthDefault : window->DC.ItemWidthStack.back(); } // Calculate default item width given value passed to PushItemWidth() or SetNextItemWidth(). @@ -7513,14 +8522,9 @@ ImVec2 ImGui::GetWindowContentRegionMax() return window->ContentRegionRect.Max - window->Pos; } -float ImGui::GetWindowContentRegionWidth() -{ - ImGuiWindow* window = GImGui->CurrentWindow; - return window->ContentRegionRect.GetWidth(); -} - // Lock horizontal starting position + capture group bounding box into one "item" (so you can use IsItemHovered() or layout primitives such as SameLine() on whole group, etc.) // Groups are currently a mishmash of functionalities which should perhaps be clarified and separated. +// FIXME-OPT: Could we safely early out on ->SkipItems? void ImGui::BeginGroup() { ImGuiContext& g = *GImGui; @@ -7536,6 +8540,7 @@ void ImGui::BeginGroup() group_data.BackupCurrLineSize = window->DC.CurrLineSize; group_data.BackupCurrLineTextBaseOffset = window->DC.CurrLineTextBaseOffset; group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive; + group_data.BackupHoveredIdIsAlive = g.HoveredId != 0; group_data.BackupActiveIdPreviousFrameIsAlive = g.ActiveIdPreviousFrameIsAlive; group_data.EmitItem = true; @@ -7544,7 +8549,7 @@ void ImGui::BeginGroup() window->DC.CursorMaxPos = window->DC.CursorPos; window->DC.CurrLineSize = ImVec2(0.0f, 0.0f); if (g.LogEnabled) - g.LogLinePosY = -FLT_MAX; // To enforce Log carriage return + g.LogLinePosY = -FLT_MAX; // To enforce a carriage return } void ImGui::EndGroup() @@ -7565,7 +8570,7 @@ void ImGui::EndGroup() window->DC.CurrLineSize = group_data.BackupCurrLineSize; window->DC.CurrLineTextBaseOffset = group_data.BackupCurrLineTextBaseOffset; if (g.LogEnabled) - g.LogLinePosY = -FLT_MAX; // To enforce Log carriage return + g.LogLinePosY = -FLT_MAX; // To enforce a carriage return if (!group_data.EmitItem) { @@ -7575,7 +8580,7 @@ void ImGui::EndGroup() window->DC.CurrLineTextBaseOffset = ImMax(window->DC.PrevLineTextBaseOffset, group_data.BackupCurrLineTextBaseOffset); // FIXME: Incorrect, we should grab the base offset from the *first line* of the group but it is hard to obtain now. ItemSize(group_bb.GetSize()); - ItemAdd(group_bb, 0); + ItemAdd(group_bb, 0, NULL, ImGuiItemFlags_NoTabStop); // If the current ActiveId was declared within the boundary of our group, we copy it to LastItemId so IsItemActive(), IsItemDeactivated() etc. will be functional on the entire group. // It would be be neater if we replaced window.DC.LastItemId by e.g. 'bool LastItemIsActive', but would put a little more burden on individual widgets. @@ -7584,19 +8589,24 @@ void ImGui::EndGroup() const bool group_contains_curr_active_id = (group_data.BackupActiveIdIsAlive != g.ActiveId) && (g.ActiveIdIsAlive == g.ActiveId) && g.ActiveId; const bool group_contains_prev_active_id = (group_data.BackupActiveIdPreviousFrameIsAlive == false) && (g.ActiveIdPreviousFrameIsAlive == true); if (group_contains_curr_active_id) - window->DC.LastItemId = g.ActiveId; + g.LastItemData.ID = g.ActiveId; else if (group_contains_prev_active_id) - window->DC.LastItemId = g.ActiveIdPreviousFrame; - window->DC.LastItemRect = group_bb; + g.LastItemData.ID = g.ActiveIdPreviousFrame; + g.LastItemData.Rect = group_bb; + + // Forward Hovered flag + const bool group_contains_curr_hovered_id = (group_data.BackupHoveredIdIsAlive == false) && g.HoveredId != 0; + if (group_contains_curr_hovered_id) + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow; // Forward Edited flag if (group_contains_curr_active_id && g.ActiveIdHasBeenEditedThisFrame) - window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Edited; + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Edited; // Forward Deactivated flag - window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HasDeactivated; + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasDeactivated; if (group_contains_prev_active_id && g.ActiveId != g.ActiveIdPreviousFrame) - window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Deactivated; + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Deactivated; g.GroupStack.pop_back(); //window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255)); // [Debug] @@ -7625,24 +8635,29 @@ static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window) ImVec2 scroll = window->Scroll; if (window->ScrollTarget.x < FLT_MAX) { + float decoration_total_width = window->ScrollbarSizes.x; float center_x_ratio = window->ScrollTargetCenterRatio.x; float scroll_target_x = window->ScrollTarget.x; - float snap_x_min = 0.0f; - float snap_x_max = window->ScrollMax.x + window->Size.x; if (window->ScrollTargetEdgeSnapDist.x > 0.0f) + { + float snap_x_min = 0.0f; + float snap_x_max = window->ScrollMax.x + window->SizeFull.x - decoration_total_width; scroll_target_x = CalcScrollEdgeSnap(scroll_target_x, snap_x_min, snap_x_max, window->ScrollTargetEdgeSnapDist.x, center_x_ratio); - scroll.x = scroll_target_x - center_x_ratio * (window->SizeFull.x - window->ScrollbarSizes.x); + } + scroll.x = scroll_target_x - center_x_ratio * (window->SizeFull.x - decoration_total_width); } if (window->ScrollTarget.y < FLT_MAX) { - float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight(); + float decoration_total_height = window->TitleBarHeight() + window->MenuBarHeight() + window->ScrollbarSizes.y; float center_y_ratio = window->ScrollTargetCenterRatio.y; float scroll_target_y = window->ScrollTarget.y; - float snap_y_min = 0.0f; - float snap_y_max = window->ScrollMax.y + window->Size.y - decoration_up_height; if (window->ScrollTargetEdgeSnapDist.y > 0.0f) + { + float snap_y_min = 0.0f; + float snap_y_max = window->ScrollMax.y + window->SizeFull.y - decoration_total_height; scroll_target_y = CalcScrollEdgeSnap(scroll_target_y, snap_y_min, snap_y_max, window->ScrollTargetEdgeSnapDist.y, center_y_ratio); - scroll.y = scroll_target_y - center_y_ratio * (window->SizeFull.y - window->ScrollbarSizes.y - decoration_up_height); + } + scroll.y = scroll_target_y - center_y_ratio * (window->SizeFull.y - decoration_total_height); } scroll.x = IM_FLOOR(ImMax(scroll.x, 0.0f)); scroll.y = IM_FLOOR(ImMax(scroll.y, 0.0f)); @@ -7654,32 +8669,80 @@ static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window) return scroll; } +void ImGui::ScrollToItem(ImGuiScrollFlags flags) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + ScrollToRectEx(window, g.LastItemData.NavRect, flags); +} + +void ImGui::ScrollToRect(ImGuiWindow* window, const ImRect& item_rect, ImGuiScrollFlags flags) +{ + ScrollToRectEx(window, item_rect, flags); +} + // Scroll to keep newly navigated item fully into view -ImVec2 ImGui::ScrollToBringRectIntoView(ImGuiWindow* window, const ImRect& item_rect) +ImVec2 ImGui::ScrollToRectEx(ImGuiWindow* window, const ImRect& item_rect, ImGuiScrollFlags flags) { ImGuiContext& g = *GImGui; ImRect window_rect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1)); //GetForegroundDrawList(window)->AddRect(window_rect.Min, window_rect.Max, IM_COL32_WHITE); // [DEBUG] - ImVec2 delta_scroll; - if (!window_rect.Contains(item_rect)) + // Check that only one behavior is selected per axis + IM_ASSERT((flags & ImGuiScrollFlags_MaskX_) == 0 || ImIsPowerOfTwo(flags & ImGuiScrollFlags_MaskX_)); + IM_ASSERT((flags & ImGuiScrollFlags_MaskY_) == 0 || ImIsPowerOfTwo(flags & ImGuiScrollFlags_MaskY_)); + + // Defaults + ImGuiScrollFlags in_flags = flags; + if ((flags & ImGuiScrollFlags_MaskX_) == 0 && window->ScrollbarX) + flags |= ImGuiScrollFlags_KeepVisibleEdgeX; + if ((flags & ImGuiScrollFlags_MaskY_) == 0) + flags |= window->Appearing ? ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeY; + + const bool fully_visible_x = item_rect.Min.x >= window_rect.Min.x && item_rect.Max.x <= window_rect.Max.x; + const bool fully_visible_y = item_rect.Min.y >= window_rect.Min.y && item_rect.Max.y <= window_rect.Max.y; + const bool can_be_fully_visible_x = (item_rect.GetWidth() + g.Style.ItemSpacing.x * 2.0f) <= window_rect.GetWidth(); + const bool can_be_fully_visible_y = (item_rect.GetHeight() + g.Style.ItemSpacing.y * 2.0f) <= window_rect.GetHeight(); + + if ((flags & ImGuiScrollFlags_KeepVisibleEdgeX) && !fully_visible_x) { - if (window->ScrollbarX && item_rect.Min.x < window_rect.Min.x) - SetScrollFromPosX(window, item_rect.Min.x - window->Pos.x - g.Style.ItemSpacing.x, 0.0f); - else if (window->ScrollbarX && item_rect.Max.x >= window_rect.Max.x) - SetScrollFromPosX(window, item_rect.Max.x - window->Pos.x + g.Style.ItemSpacing.x, 1.0f); - if (item_rect.Min.y < window_rect.Min.y) - SetScrollFromPosY(window, item_rect.Min.y - window->Pos.y - g.Style.ItemSpacing.y, 0.0f); - else if (item_rect.Max.y >= window_rect.Max.y) - SetScrollFromPosY(window, item_rect.Max.y - window->Pos.y + g.Style.ItemSpacing.y, 1.0f); + if (item_rect.Min.x < window_rect.Min.x || !can_be_fully_visible_x) + SetScrollFromPosX(window, item_rect.Min.x - g.Style.ItemSpacing.x - window->Pos.x, 0.0f); + else if (item_rect.Max.x >= window_rect.Max.x) + SetScrollFromPosX(window, item_rect.Max.x + g.Style.ItemSpacing.x - window->Pos.x, 1.0f); + } + else if (((flags & ImGuiScrollFlags_KeepVisibleCenterX) && !fully_visible_x) || (flags & ImGuiScrollFlags_AlwaysCenterX)) + { + float target_x = can_be_fully_visible_x ? ImFloor((item_rect.Min.x + item_rect.Max.x - window->InnerRect.GetWidth()) * 0.5f) : item_rect.Min.x; + SetScrollFromPosX(window, target_x - window->Pos.x, 0.0f); + } - ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window); - delta_scroll = next_scroll - window->Scroll; + if ((flags & ImGuiScrollFlags_KeepVisibleEdgeY) && !fully_visible_y) + { + if (item_rect.Min.y < window_rect.Min.y || !can_be_fully_visible_y) + SetScrollFromPosY(window, item_rect.Min.y - g.Style.ItemSpacing.y - window->Pos.y, 0.0f); + else if (item_rect.Max.y >= window_rect.Max.y) + SetScrollFromPosY(window, item_rect.Max.y + g.Style.ItemSpacing.y - window->Pos.y, 1.0f); + } + else if (((flags & ImGuiScrollFlags_KeepVisibleCenterY) && !fully_visible_y) || (flags & ImGuiScrollFlags_AlwaysCenterY)) + { + float target_y = can_be_fully_visible_y ? ImFloor((item_rect.Min.y + item_rect.Max.y - window->InnerRect.GetHeight()) * 0.5f) : item_rect.Min.y; + SetScrollFromPosY(window, target_y - window->Pos.y, 0.0f); } + ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window); + ImVec2 delta_scroll = next_scroll - window->Scroll; + // Also scroll parent window to keep us into view if necessary - if (window->Flags & ImGuiWindowFlags_ChildWindow) - delta_scroll += ScrollToBringRectIntoView(window->ParentWindow, ImRect(item_rect.Min - delta_scroll, item_rect.Max - delta_scroll)); + if (!(flags & ImGuiScrollFlags_NoScrollParent) && (window->Flags & ImGuiWindowFlags_ChildWindow)) + { + // FIXME-SCROLL: May be an option? + if ((in_flags & (ImGuiScrollFlags_AlwaysCenterX | ImGuiScrollFlags_KeepVisibleCenterX)) != 0) + in_flags = (in_flags & ~ImGuiScrollFlags_MaskX_) | ImGuiScrollFlags_KeepVisibleEdgeX; + if ((in_flags & (ImGuiScrollFlags_AlwaysCenterY | ImGuiScrollFlags_KeepVisibleCenterY)) != 0) + in_flags = (in_flags & ~ImGuiScrollFlags_MaskY_) | ImGuiScrollFlags_KeepVisibleEdgeY; + delta_scroll += ScrollToRectEx(window->ParentWindow, ImRect(item_rect.Min - delta_scroll, item_rect.Max - delta_scroll), in_flags); + } return delta_scroll; } @@ -7755,7 +8818,8 @@ void ImGui::SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x void ImGui::SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio) { IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f); - local_y -= window->TitleBarHeight() + window->MenuBarHeight(); // FIXME: Would be nice to have a more standardized access to our scrollable/client rect + const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight(); // FIXME: Would be nice to have a more standardized access to our scrollable/client rect; + local_y -= decoration_up_height; window->ScrollTarget.y = IM_FLOOR(local_y + window->Scroll.y); // Convert local position to scroll offset window->ScrollTargetCenterRatio.y = center_y_ratio; window->ScrollTargetEdgeSnapDist.y = 0.0f; @@ -7778,8 +8842,8 @@ void ImGui::SetScrollHereX(float center_x_ratio) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - float spacing_x = g.Style.ItemSpacing.x; - float target_pos_x = ImLerp(window->DC.LastItemRect.Min.x - spacing_x, window->DC.LastItemRect.Max.x + spacing_x, center_x_ratio); + float spacing_x = ImMax(window->WindowPadding.x, g.Style.ItemSpacing.x); + float target_pos_x = ImLerp(g.LastItemData.Rect.Min.x - spacing_x, g.LastItemData.Rect.Max.x + spacing_x, center_x_ratio); SetScrollFromPosX(window, target_pos_x - window->Pos.x, center_x_ratio); // Convert from absolute to local pos // Tweak: snap on edges when aiming at an item very close to the edge @@ -7791,7 +8855,7 @@ void ImGui::SetScrollHereY(float center_y_ratio) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - float spacing_y = g.Style.ItemSpacing.y; + float spacing_y = ImMax(window->WindowPadding.y, g.Style.ItemSpacing.y); float target_pos_y = ImLerp(window->DC.CursorPosPrevLine.y - spacing_y, window->DC.CursorPosPrevLine.y + window->DC.PrevLineSize.y + spacing_y, center_y_ratio); SetScrollFromPosY(window, target_pos_y - window->Pos.y, center_y_ratio); // Convert from absolute to local pos @@ -7805,10 +8869,10 @@ void ImGui::SetScrollHereY(float center_y_ratio) void ImGui::BeginTooltip() { - BeginTooltipEx(ImGuiWindowFlags_None, ImGuiTooltipFlags_None); + BeginTooltipEx(ImGuiTooltipFlags_None, ImGuiWindowFlags_None); } -void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, ImGuiTooltipFlags tooltip_flags) +void ImGui::BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags extra_window_flags) { ImGuiContext& g = *GImGui; @@ -7837,7 +8901,7 @@ void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, ImGuiTooltipFlags toolt ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", ++g.TooltipOverrideCount); } ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize; - Begin(window_name, NULL, flags | extra_flags); + Begin(window_name, NULL, flags | extra_window_flags); } void ImGui::EndTooltip() @@ -7848,7 +8912,7 @@ void ImGui::EndTooltip() void ImGui::SetTooltipV(const char* fmt, va_list args) { - BeginTooltipEx(0, ImGuiTooltipFlags_OverridePreviousTooltip); + BeginTooltipEx(ImGuiTooltipFlags_OverridePreviousTooltip, ImGuiWindowFlags_None); TextV(fmt, args); EndTooltip(); } @@ -7916,12 +8980,27 @@ ImGuiWindow* ImGui::GetTopMostPopupModal() return NULL; } +ImGuiWindow* ImGui::GetTopMostAndVisiblePopupModal() +{ + ImGuiContext& g = *GImGui; + for (int n = g.OpenPopupStack.Size - 1; n >= 0; n--) + if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window) + if ((popup->Flags & ImGuiWindowFlags_Modal) && IsWindowActiveAndVisible(popup)) + return popup; + return NULL; +} + void ImGui::OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags) { ImGuiContext& g = *GImGui; OpenPopupEx(g.CurrentWindow->GetID(str_id), popup_flags); } +void ImGui::OpenPopup(ImGuiID id, ImGuiPopupFlags popup_flags) +{ + OpenPopupEx(id, popup_flags); +} + // Mark popup as open (toggle toward open state). // Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block. // Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level). @@ -8003,7 +9082,7 @@ void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to bool ref_window_is_descendent_of_popup = false; for (int n = popup_count_to_keep; n < g.OpenPopupStack.Size; n++) if (ImGuiWindow* popup_window = g.OpenPopupStack[n].Window) - if (popup_window->RootWindow == ref_window->RootWindow) + if (IsWindowWithinBeginStackOf(ref_window, popup_window)) { ref_window_is_descendent_of_popup = true; break; @@ -8019,6 +9098,21 @@ void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to } } +void ImGui::ClosePopupsExceptModals() +{ + ImGuiContext& g = *GImGui; + + int popup_count_to_keep; + for (popup_count_to_keep = g.OpenPopupStack.Size; popup_count_to_keep > 0; popup_count_to_keep--) + { + ImGuiWindow* window = g.OpenPopupStack[popup_count_to_keep - 1].Window; + if (!window || window->Flags & ImGuiWindowFlags_Modal) + break; + } + if (popup_count_to_keep < g.OpenPopupStack.Size) // This test is not required but it allows to set a convenient breakpoint on the statement below + ClosePopupToLevel(popup_count_to_keep, true); +} + void ImGui::ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup) { ImGuiContext& g = *GImGui; @@ -8061,7 +9155,7 @@ void ImGui::CloseCurrentPopup() ImGuiWindow* parent_popup_window = g.OpenPopupStack[popup_idx - 1].Window; bool close_parent = false; if (popup_window && (popup_window->Flags & ImGuiWindowFlags_ChildMenu)) - if (parent_popup_window == NULL || !(parent_popup_window->Flags & ImGuiWindowFlags_Modal)) + if (parent_popup_window && !(parent_popup_window->Flags & ImGuiWindowFlags_MenuBar)) close_parent = true; if (!close_parent) break; @@ -8089,7 +9183,7 @@ bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags flags) char name[20]; if (flags & ImGuiWindowFlags_ChildMenu) - ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.BeginPopupStack.Size); // Recycle windows based on depth + ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.BeginMenuCount); // Recycle windows based on depth else ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame @@ -8110,7 +9204,8 @@ bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags) return false; } flags |= ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings; - return BeginPopupEx(g.CurrentWindow->GetID(str_id), flags); + ImGuiID id = g.CurrentWindow->GetID(str_id); + return BeginPopupEx(id, flags); } // If 'p_open' is specified for a modal popup window, the popup will have a regular close button which will close the popup. @@ -8130,7 +9225,10 @@ bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags fla // (this won't really last as settings will kick in, and is mostly for backward compatibility. user may do the same themselves) // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window. if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) == 0) - SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_FirstUseEver, ImVec2(0.5f, 0.5f)); + { + const ImGuiViewport* viewport = GetMainViewport(); + SetNextWindowPos(viewport->GetCenter(), ImGuiCond_FirstUseEver, ImVec2(0.5f, 0.5f)); + } flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse; const bool is_open = Begin(name, p_open, flags); @@ -8151,7 +9249,7 @@ void ImGui::EndPopup() IM_ASSERT(window->Flags & ImGuiWindowFlags_Popup); // Mismatched BeginPopup()/EndPopup() calls IM_ASSERT(g.BeginPopupStack.Size > 0); - // Make all menus and popups wrap around for now, may need to expose that policy. + // Make all menus and popups wrap around for now, may need to expose that policy (e.g. focus scope could include wrap/loop policy flags used by new move requests) if (g.NavWindow == window) NavMoveRequestTryWrapping(window, ImGuiNavMoveFlags_LoopY); @@ -8167,28 +9265,41 @@ void ImGui::EndPopup() // - This is essentially the same as BeginPopupContextItem() but without the trailing BeginPopup() void ImGui::OpenPopupOnItemClick(const char* str_id, ImGuiPopupFlags popup_flags) { - ImGuiWindow* window = GImGui->CurrentWindow; + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_); if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) { - ImGuiID id = str_id ? window->GetID(str_id) : window->DC.LastItemId; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict! - IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item) + ImGuiID id = str_id ? window->GetID(str_id) : g.LastItemData.ID; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict! + IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item) OpenPopupEx(id, popup_flags); } } // This is a helper to handle the simplest case of associating one named popup to one given widget. -// - You can pass a NULL str_id to use the identifier of the last item. -// - You may want to handle this on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters). -// - This is essentially the same as calling OpenPopupOnItemClick() + BeginPopup() but written to avoid -// computing the ID twice because BeginPopupContextXXX functions may be called very frequently. +// - To create a popup associated to the last item, you generally want to pass a NULL value to str_id. +// - To create a popup with a specific identifier, pass it in str_id. +// - This is useful when using using BeginPopupContextItem() on an item which doesn't have an identifier, e.g. a Text() call. +// - This is useful when multiple code locations may want to manipulate/open the same popup, given an explicit id. +// - You may want to handle the whole on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters). +// This is essentially the same as: +// id = str_id ? GetID(str_id) : GetItemID(); +// OpenPopupOnItemClick(str_id, ImGuiPopupFlags_MouseButtonRight); +// return BeginPopup(id); +// Which is essentially the same as: +// id = str_id ? GetID(str_id) : GetItemID(); +// if (IsItemHovered() && IsMouseReleased(ImGuiMouseButton_Right)) +// OpenPopup(id); +// return BeginPopup(id); +// The main difference being that this is tweaked to avoid computing the ID twice. bool ImGui::BeginPopupContextItem(const char* str_id, ImGuiPopupFlags popup_flags) { - ImGuiWindow* window = GImGui->CurrentWindow; + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; if (window->SkipItems) return false; - ImGuiID id = str_id ? window->GetID(str_id) : window->DC.LastItemId; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict! - IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item) + ImGuiID id = str_id ? window->GetID(str_id) : g.LastItemData.ID; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict! + IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item) int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_); if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) OpenPopupEx(id, popup_flags); @@ -8197,7 +9308,8 @@ bool ImGui::BeginPopupContextItem(const char* str_id, ImGuiPopupFlags popup_flag bool ImGui::BeginPopupContextWindow(const char* str_id, ImGuiPopupFlags popup_flags) { - ImGuiWindow* window = GImGui->CurrentWindow; + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; if (!str_id) str_id = "window_context"; ImGuiID id = window->GetID(str_id); @@ -8210,7 +9322,8 @@ bool ImGui::BeginPopupContextWindow(const char* str_id, ImGuiPopupFlags popup_fl bool ImGui::BeginPopupContextVoid(const char* str_id, ImGuiPopupFlags popup_flags) { - ImGuiWindow* window = GImGui->CurrentWindow; + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; if (!str_id) str_id = "void_context"; ImGuiID id = window->GetID(str_id); @@ -8223,6 +9336,9 @@ bool ImGui::BeginPopupContextVoid(const char* str_id, ImGuiPopupFlags popup_flag // r_avoid = the rectangle to avoid (e.g. for tooltip it is a rectangle around the mouse cursor which we want to avoid. for popups it's a small point around the cursor.) // r_outer = the visible area rectangle, minus safe area padding. If our popup size won't fit because of safe area padding we ignore it. +// (r_outer is usually equivalent to the viewport rectangle minus padding, but when multi-viewports are enabled and monitor +// information are available, it may represent the entire platform monitor from the frame of reference of the current viewport. +// this allows us to have tooltips/popups displayed out of the parent viewport.) ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy) { ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size); @@ -8297,11 +9413,13 @@ ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& s return pos; } -ImRect ImGui::GetWindowAllowedExtentRect(ImGuiWindow* window) +// Note that this is used for popups, which can overlap the non work-area of individual viewports. +ImRect ImGui::GetPopupAllowedExtentRect(ImGuiWindow* window) { + ImGuiContext& g = *GImGui; IM_UNUSED(window); - ImVec2 padding = GImGui->Style.DisplaySafeAreaPadding; - ImRect r_screen = GetViewportRect(); + ImRect r_screen = ((ImGuiViewportP*)(void*)GetMainViewport())->GetMainRect(); + ImVec2 padding = g.Style.DisplaySafeAreaPadding; r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f)); return r_screen; } @@ -8310,13 +9428,13 @@ ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window) { ImGuiContext& g = *GImGui; - ImRect r_outer = GetWindowAllowedExtentRect(window); + ImRect r_outer = GetPopupAllowedExtentRect(window); if (window->Flags & ImGuiWindowFlags_ChildMenu) { // Child menus typically request _any_ position within the parent menu item, and then we move the new menu outside the parent bounds. // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu. IM_ASSERT(g.CurrentWindow == window); - ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2]; + ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2].Window; float horizontal_overlap = g.Style.ItemInnerSpacing.x; // We want some overlap to convey the relative depth of each menu (currently the amount of overlap is hard-coded to style.ItemSpacing.x). ImRect r_avoid; if (parent_window->DC.MenuBarAppending) @@ -8327,8 +9445,7 @@ ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window) } if (window->Flags & ImGuiWindowFlags_Popup) { - ImRect r_avoid = ImRect(window->Pos.x - 1, window->Pos.y - 1, window->Pos.x + 1, window->Pos.y + 1); - return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Default); + return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, ImRect(window->Pos, window->Pos), ImGuiPopupPositionPolicy_Default); // Ideally we'd disable r_avoid here } if (window->Flags & ImGuiWindowFlags_Tooltip) { @@ -8350,26 +9467,18 @@ ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window) // [SECTION] KEYBOARD/GAMEPAD NAVIGATION //----------------------------------------------------------------------------- -// FIXME-NAV: The existence of SetNavID vs SetNavIDWithRectRel vs SetFocusID is incredibly messy and confusing, -// and needs some explanation or serious refactoring. -void ImGui::SetNavID(ImGuiID id, int nav_layer, ImGuiID focus_scope_id) +// FIXME-NAV: The existence of SetNavID vs SetFocusID properly needs to be clarified/reworked. +// In our terminology those should be interchangeable. Those two functions are merely a legacy artifact, so at minimum naming should be clarified. +void ImGui::SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel) { ImGuiContext& g = *GImGui; - IM_ASSERT(g.NavWindow); - IM_ASSERT(nav_layer == 0 || nav_layer == 1); + IM_ASSERT(g.NavWindow != NULL); + IM_ASSERT(nav_layer == ImGuiNavLayer_Main || nav_layer == ImGuiNavLayer_Menu); g.NavId = id; + g.NavLayer = nav_layer; g.NavFocusScopeId = focus_scope_id; g.NavWindow->NavLastIds[nav_layer] = id; -} - -void ImGui::SetNavIDWithRectRel(ImGuiID id, int nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel) -{ - ImGuiContext& g = *GImGui; - SetNavID(id, nav_layer, focus_scope_id); g.NavWindow->NavRectRel[nav_layer] = rect_rel; - g.NavMousePosDirty = true; - g.NavDisableHighlight = false; - g.NavDisableMouseHover = true; } void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window) @@ -8387,8 +9496,8 @@ void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window) g.NavLayer = nav_layer; g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent; window->NavLastIds[nav_layer] = id; - if (window->DC.LastItemId == id) - window->NavRectRel[nav_layer] = ImRect(window->DC.LastItemRect.Min - window->Pos, window->DC.LastItemRect.Max - window->Pos); + if (g.LastItemData.ID == id) + window->NavRectRel[nav_layer] = WindowRectAbsToRel(window, g.LastItemData.NavRect); if (g.ActiveIdSource == ImGuiInputSource_Nav) g.NavDisableMouseHover = true; @@ -8419,7 +9528,7 @@ static void inline NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir, ImRect r.Min.y = ImClamp(r.Min.y, clip_rect.Min.y, clip_rect.Max.y); r.Max.y = ImClamp(r.Max.y, clip_rect.Min.y, clip_rect.Max.y); } - else + else // FIXME: PageUp/PageDown are leaving move_dir == None { r.Min.x = ImClamp(r.Min.x, clip_rect.Min.x, clip_rect.Max.x); r.Max.x = ImClamp(r.Max.x, clip_rect.Min.x, clip_rect.Max.x); @@ -8427,15 +9536,17 @@ static void inline NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir, ImRect } // Scoring function for gamepad/keyboard directional navigation. Based on https://gist.github.com/rygorous/6981057 -static bool ImGui::NavScoreItem(ImGuiNavMoveResult* result, ImRect cand) +static bool ImGui::NavScoreItem(ImGuiNavItemData* result) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; if (g.NavLayer != window->DC.NavLayerCurrent) return false; - const ImRect& curr = g.NavScoringRect; // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having varied item width) - g.NavScoringCount++; + // FIXME: Those are not good variables names + ImRect cand = g.LastItemData.NavRect; // Current item nav rectangle + const ImRect curr = g.NavScoringRect; // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having varied item width) + g.NavScoringDebugCount++; // When entering through a NavFlattened border, we consider child window items as fully clipped for scoring if (window->ParentWindow == g.NavWindow) @@ -8485,7 +9596,7 @@ static bool ImGui::NavScoreItem(ImGuiNavMoveResult* result, ImRect cand) else { // Degenerate case: two overlapping buttons with same center, break ties arbitrarily (note that LastItemId here is really the _previous_ item order, but it doesn't matter) - quadrant = (window->DC.LastItemId < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right; + quadrant = (g.LastItemData.ID < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right; } #if IMGUI_DEBUG_NAV_SCORING @@ -8497,24 +9608,24 @@ static bool ImGui::NavScoreItem(ImGuiNavMoveResult* result, ImRect cand) draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255,200,0,100)); draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200)); draw_list->AddRectFilled(cand.Max - ImVec2(4, 4), cand.Max + CalcTextSize(buf) + ImVec2(4, 4), IM_COL32(40,0,0,150)); - draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Max, ~0U, buf); + draw_list->AddText(cand.Max, ~0U, buf); } else if (g.IO.KeyCtrl) // Hold to preview score in matching quadrant. Press C to rotate. { - if (IsKeyPressedMap(ImGuiKey_C)) { g.NavMoveDirLast = (ImGuiDir)((g.NavMoveDirLast + 1) & 3); g.IO.KeysDownDuration[g.IO.KeyMap[ImGuiKey_C]] = 0.01f; } if (quadrant == g.NavMoveDir) { ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center); ImDrawList* draw_list = GetForegroundDrawList(window); draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 200)); - draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Min, IM_COL32(255, 255, 255, 255), buf); + draw_list->AddText(cand.Min, IM_COL32(255, 255, 255, 255), buf); } } #endif // Is it in the quadrant we're interesting in moving to? bool new_best = false; - if (quadrant == g.NavMoveDir) + const ImGuiDir move_dir = g.NavMoveDir; + if (quadrant == move_dir) { // Does it beat the current best candidate? if (dist_box < result->DistBox) @@ -8536,7 +9647,7 @@ static bool ImGui::NavScoreItem(ImGuiNavMoveResult* result, ImRect cand) // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items // (with higher index) to the right/downwards by an infinitesimal amount since we the current "best" button already (so it must have a lower index), // this is fairly easy. This rule ensures that all buttons with dx==dy==0 will end up being linked in order of appearance along the x axis. - if (((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance + if (((move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance new_best = true; } } @@ -8549,7 +9660,7 @@ static bool ImGui::NavScoreItem(ImGuiNavMoveResult* result, ImRect cand) // Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option? if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial) // Check axial match if (g.NavLayer == ImGuiNavLayer_Menu && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu)) - if ((g.NavMoveDir == ImGuiDir_Left && dax < 0.0f) || (g.NavMoveDir == ImGuiDir_Right && dax > 0.0f) || (g.NavMoveDir == ImGuiDir_Up && day < 0.0f) || (g.NavMoveDir == ImGuiDir_Down && day > 0.0f)) + if ((move_dir == ImGuiDir_Left && dax < 0.0f) || (move_dir == ImGuiDir_Right && dax > 0.0f) || (move_dir == ImGuiDir_Up && day < 0.0f) || (move_dir == ImGuiDir_Down && day > 0.0f)) { result->DistAxial = dist_axial; new_best = true; @@ -8558,34 +9669,38 @@ static bool ImGui::NavScoreItem(ImGuiNavMoveResult* result, ImRect cand) return new_best; } -static void ImGui::NavApplyItemToResult(ImGuiNavMoveResult* result, ImGuiWindow* window, ImGuiID id, const ImRect& nav_bb_rel) +static void ImGui::NavApplyItemToResult(ImGuiNavItemData* result) { + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; result->Window = window; - result->ID = id; + result->ID = g.LastItemData.ID; result->FocusScopeId = window->DC.NavFocusScopeIdCurrent; - result->RectRel = nav_bb_rel; + result->InFlags = g.LastItemData.InFlags; + result->RectRel = WindowRectAbsToRel(window, g.LastItemData.NavRect); } // We get there when either NavId == id, or when g.NavAnyRequest is set (which is updated by NavUpdateAnyRequestFlag above) -static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id) +// This is called after LastItemData is set. +static void ImGui::NavProcessItem() { ImGuiContext& g = *GImGui; - //if (!g.IO.NavActive) // [2017/10/06] Removed this possibly redundant test but I am not sure of all the side-effects yet. Some of the feature here will need to work regardless of using a _NoNavInputs flag. - // return; - - const ImGuiItemFlags item_flags = window->DC.ItemFlags; - const ImRect nav_bb_rel(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos); + ImGuiWindow* window = g.CurrentWindow; + const ImGuiID id = g.LastItemData.ID; + const ImRect nav_bb = g.LastItemData.NavRect; + const ImGuiItemFlags item_flags = g.LastItemData.InFlags; // Process Init Request if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent) { // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback - if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus) || g.NavInitResultId == 0) + const bool candidate_for_nav_default_focus = (item_flags & (ImGuiItemFlags_NoNavDefaultFocus | ImGuiItemFlags_Disabled)) == 0; + if (candidate_for_nav_default_focus || g.NavInitResultId == 0) { g.NavInitResultId = id; - g.NavInitResultRectRel = nav_bb_rel; + g.NavInitResultRectRel = WindowRectAbsToRel(window, nav_bb); } - if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus)) + if (candidate_for_nav_default_focus) { g.NavInitRequest = false; // Found a match, clear request NavUpdateAnyRequestFlag(); @@ -8593,27 +9708,32 @@ static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, con } // Process Move Request (scoring for navigation) - // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRectScreen + scoring from a rect wrapped according to current wrapping policy) - if ((g.NavId != id || (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & (ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNav))) + // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRect + scoring from a rect wrapped according to current wrapping policy) + if (g.NavMoveScoringItems) { - ImGuiNavMoveResult* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; -#if IMGUI_DEBUG_NAV_SCORING - // [DEBUG] Score all items in NavWindow at all times - if (!g.NavMoveRequest) - g.NavMoveDir = g.NavMoveDirLast; - bool new_best = NavScoreItem(result, nav_bb) && g.NavMoveRequest; -#else - bool new_best = g.NavMoveRequest && NavScoreItem(result, nav_bb); -#endif - if (new_best) - NavApplyItemToResult(result, window, id, nav_bb_rel); - - // Features like PageUp/PageDown need to maintain a separate score for the visible set of items. - const float VISIBLE_RATIO = 0.70f; - if ((g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb)) - if (ImClamp(nav_bb.Max.y, window->ClipRect.Min.y, window->ClipRect.Max.y) - ImClamp(nav_bb.Min.y, window->ClipRect.Min.y, window->ClipRect.Max.y) >= (nav_bb.Max.y - nav_bb.Min.y) * VISIBLE_RATIO) - if (NavScoreItem(&g.NavMoveResultLocalVisibleSet, nav_bb)) - NavApplyItemToResult(&g.NavMoveResultLocalVisibleSet, window, id, nav_bb_rel); + const bool is_tab_stop = (item_flags & ImGuiItemFlags_Inputable) && (item_flags & (ImGuiItemFlags_NoTabStop | ImGuiItemFlags_Disabled)) == 0; + const bool is_tabbing = (g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) != 0; + if (is_tabbing) + { + if (is_tab_stop || (g.NavMoveFlags & ImGuiNavMoveFlags_FocusApi)) + NavProcessItemForTabbingRequest(id); + } + else if ((g.NavId != id || (g.NavMoveFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & (ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNav))) + { + ImGuiNavItemData* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; + if (!is_tabbing) + { + if (NavScoreItem(result)) + NavApplyItemToResult(result); + + // Features like PageUp/PageDown need to maintain a separate score for the visible set of items. + const float VISIBLE_RATIO = 0.70f; + if ((g.NavMoveFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb)) + if (ImClamp(nav_bb.Max.y, window->ClipRect.Min.y, window->ClipRect.Max.y) - ImClamp(nav_bb.Min.y, window->ClipRect.Min.y, window->ClipRect.Max.y) >= (nav_bb.Max.y - nav_bb.Min.y) * VISIBLE_RATIO) + if (NavScoreItem(&g.NavMoveResultLocalVisible)) + NavApplyItemToResult(&g.NavMoveResultLocalVisible); + } + } } // Update window-relative bounding box of navigated item @@ -8623,44 +9743,124 @@ static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, con g.NavLayer = window->DC.NavLayerCurrent; g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent; g.NavIdIsAlive = true; - g.NavIdTabCounter = window->DC.FocusCounterTabStop; - window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel; // Store item bounding box (relative to window position) + window->NavRectRel[window->DC.NavLayerCurrent] = WindowRectAbsToRel(window, nav_bb); // Store item bounding box (relative to window position) + } +} + +// Handle "scoring" of an item for a tabbing/focusing request initiated by NavUpdateCreateTabbingRequest(). +// Note that SetKeyboardFocusHere() API calls are considered tabbing requests! +// - Case 1: no nav/active id: set result to first eligible item, stop storing. +// - Case 2: tab forward: on ref id set counter, on counter elapse store result +// - Case 3: tab forward wrap: set result to first eligible item (preemptively), on ref id set counter, on next frame if counter hasn't elapsed store result. // FIXME-TABBING: Could be done as a next-frame forwarded request +// - Case 4: tab backward: store all results, on ref id pick prev, stop storing +// - Case 5: tab backward wrap: store all results, on ref id if no result keep storing until last // FIXME-TABBING: Could be done as next-frame forwarded requested +void ImGui::NavProcessItemForTabbingRequest(ImGuiID id) +{ + ImGuiContext& g = *GImGui; + + // Always store in NavMoveResultLocal (unlike directional request which uses NavMoveResultOther on sibling/flattened windows) + ImGuiNavItemData* result = &g.NavMoveResultLocal; + if (g.NavTabbingDir == +1) + { + // Tab Forward or SetKeyboardFocusHere() with >= 0 + if (g.NavTabbingResultFirst.ID == 0) + NavApplyItemToResult(&g.NavTabbingResultFirst); + if (--g.NavTabbingCounter == 0) + NavMoveRequestResolveWithLastItem(result); + else if (g.NavId == id) + g.NavTabbingCounter = 1; + } + else if (g.NavTabbingDir == -1) + { + // Tab Backward + if (g.NavId == id) + { + if (result->ID) + { + g.NavMoveScoringItems = false; + NavUpdateAnyRequestFlag(); + } + } + else + { + NavApplyItemToResult(result); + } + } + else if (g.NavTabbingDir == 0) + { + // Tab Init + if (g.NavTabbingResultFirst.ID == 0) + NavMoveRequestResolveWithLastItem(&g.NavTabbingResultFirst); } } bool ImGui::NavMoveRequestButNoResultYet() { ImGuiContext& g = *GImGui; - return g.NavMoveRequest && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0; + return g.NavMoveScoringItems && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0; +} + +// FIXME: ScoringRect is not set +void ImGui::NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(g.NavWindow != NULL); + + if (move_flags & ImGuiNavMoveFlags_Tabbing) + move_flags |= ImGuiNavMoveFlags_AllowCurrentNavId; + + g.NavMoveSubmitted = g.NavMoveScoringItems = true; + g.NavMoveDir = move_dir; + g.NavMoveDirForDebug = move_dir; + g.NavMoveClipDir = clip_dir; + g.NavMoveFlags = move_flags; + g.NavMoveScrollFlags = scroll_flags; + g.NavMoveForwardToNextFrame = false; + g.NavMoveKeyMods = g.IO.KeyMods; + g.NavTabbingCounter = 0; + g.NavMoveResultLocal.Clear(); + g.NavMoveResultLocalVisible.Clear(); + g.NavMoveResultOther.Clear(); + NavUpdateAnyRequestFlag(); +} + +void ImGui::NavMoveRequestResolveWithLastItem(ImGuiNavItemData* result) +{ + ImGuiContext& g = *GImGui; + g.NavMoveScoringItems = false; // Ensure request doesn't need more processing + NavApplyItemToResult(result); + NavUpdateAnyRequestFlag(); } void ImGui::NavMoveRequestCancel() { ImGuiContext& g = *GImGui; - g.NavMoveRequest = false; + g.NavMoveSubmitted = g.NavMoveScoringItems = false; NavUpdateAnyRequestFlag(); } -void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, const ImRect& bb_rel, ImGuiNavMoveFlags move_flags) +// Forward will reuse the move request again on the next frame (generally with modifications done to it) +void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags) { ImGuiContext& g = *GImGui; - IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_None); + IM_ASSERT(g.NavMoveForwardToNextFrame == false); NavMoveRequestCancel(); + g.NavMoveForwardToNextFrame = true; g.NavMoveDir = move_dir; g.NavMoveClipDir = clip_dir; - g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued; - g.NavMoveRequestFlags = move_flags; - g.NavWindow->NavRectRel[g.NavLayer] = bb_rel; + g.NavMoveFlags = move_flags | ImGuiNavMoveFlags_Forwarded; + g.NavMoveScrollFlags = scroll_flags; } -void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags) +// Navigation wrap-around logic is delayed to the end of the frame because this operation is only valid after entire +// popup is assembled and in case of appended popups it is not clear which EndPopup() call is final. +void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags wrap_flags) { ImGuiContext& g = *GImGui; - - // Navigation wrap-around logic is delayed to the end of the frame because this operation is only valid after entire - // popup is assembled and in case of appended popups it is not clear which EndPopup() call is final. - g.NavWrapRequestWindow = window; - g.NavWrapRequestFlags = move_flags; + IM_ASSERT(wrap_flags != 0); // Call with _WrapX, _WrapY, _LoopX, _LoopY + // In theory we should test for NavMoveRequestButNoResultYet() but there's no point doing it, NavEndFrame() will do the same test + if (g.NavWindow == window && g.NavMoveScoringItems && g.NavLayer == ImGuiNavLayer_Main) + g.NavMoveFlags |= wrap_flags; } // FIXME: This could be replaced by updating a frame number in each window when (window == NavWindow) and (NavLayer == 0). @@ -8668,7 +9868,7 @@ void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags mov static void ImGui::NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window) { ImGuiWindow* parent = nav_window; - while (parent && (parent->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (parent->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0) + while (parent && parent->RootWindow != parent && (parent->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0) parent = parent->ParentWindow; if (parent && parent != nav_window) parent->NavLastChildNavWindow = nav_window; @@ -8683,23 +9883,34 @@ static ImGuiWindow* ImGui::NavRestoreLastChildNavWindow(ImGuiWindow* window) return window; } -static void NavRestoreLayer(ImGuiNavLayer layer) +void ImGui::NavRestoreLayer(ImGuiNavLayer layer) { ImGuiContext& g = *GImGui; - g.NavLayer = layer; - if (layer == 0) - g.NavWindow = ImGui::NavRestoreLastChildNavWindow(g.NavWindow); + if (layer == ImGuiNavLayer_Main) + g.NavWindow = NavRestoreLastChildNavWindow(g.NavWindow); ImGuiWindow* window = g.NavWindow; - if (layer == 0 && window->NavLastIds[0] != 0) - ImGui::SetNavIDWithRectRel(window->NavLastIds[0], layer, 0, window->NavRectRel[0]); + if (window->NavLastIds[layer] != 0) + { + SetNavID(window->NavLastIds[layer], layer, 0, window->NavRectRel[layer]); + } else - ImGui::NavInitWindow(window, true); + { + g.NavLayer = layer; + NavInitWindow(window, true); + } +} + +void ImGui::NavRestoreHighlightAfterMove() +{ + ImGuiContext& g = *GImGui; + g.NavDisableHighlight = false; + g.NavDisableMouseHover = g.NavMousePosDirty = true; } static inline void ImGui::NavUpdateAnyRequestFlag() { ImGuiContext& g = *GImGui; - g.NavAnyRequest = g.NavMoveRequest || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL); + g.NavAnyRequest = g.NavMoveScoringItems || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL); if (g.NavAnyRequest) IM_ASSERT(g.NavWindow != NULL); } @@ -8709,14 +9920,20 @@ void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit) { ImGuiContext& g = *GImGui; IM_ASSERT(window == g.NavWindow); + + if (window->Flags & ImGuiWindowFlags_NoNavInputs) + { + g.NavId = g.NavFocusScopeId = 0; + return; + } + bool init_for_nav = false; - if (!(window->Flags & ImGuiWindowFlags_NoNavInputs)) - if (!(window->Flags & ImGuiWindowFlags_ChildWindow) || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit) - init_for_nav = true; + if (window == window->RootWindow || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit) + init_for_nav = true; IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from NavInitWindow(), init_for_nav=%d, window=\"%s\", layer=%d\n", init_for_nav, window->Name, g.NavLayer); if (init_for_nav) { - SetNavID(0, g.NavLayer, 0); + SetNavID(0, g.NavLayer, 0, ImRect()); g.NavInitRequest = true; g.NavInitRequestFromMove = false; g.NavInitResultId = 0; @@ -8733,23 +9950,43 @@ void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit) static ImVec2 ImGui::NavCalcPreferredRefPos() { ImGuiContext& g = *GImGui; - if (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow) + ImGuiWindow* window = g.NavWindow; + if (g.NavDisableHighlight || !g.NavDisableMouseHover || !window) { // Mouse (we need a fallback in case the mouse becomes invalid after being used) - if (IsMousePosValid(&g.IO.MousePos)) - return g.IO.MousePos; - return g.LastValidMousePos; + // The +1.0f offset when stored by OpenPopupEx() allows reopening this or another popup (same or another mouse button) while not moving the mouse, it is pretty standard. + // In theory we could move that +1.0f offset in OpenPopupEx() + ImVec2 p = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : g.MouseLastValidPos; + return ImVec2(p.x + 1.0f, p.y); } else { - // When navigation is active and mouse is disabled, decide on an arbitrary position around the bottom left of the currently navigated item. - const ImRect& rect_rel = g.NavWindow->NavRectRel[g.NavLayer]; - ImVec2 pos = g.NavWindow->Pos + ImVec2(rect_rel.Min.x + ImMin(g.Style.FramePadding.x * 4, rect_rel.GetWidth()), rect_rel.Max.y - ImMin(g.Style.FramePadding.y, rect_rel.GetHeight())); - ImRect visible_rect = GetViewportRect(); - return ImFloor(ImClamp(pos, visible_rect.Min, visible_rect.Max)); // ImFloor() is important because non-integer mouse position application in backend might be lossy and result in undesirable non-zero delta. + // When navigation is active and mouse is disabled, pick a position around the bottom left of the currently navigated item + // Take account of upcoming scrolling (maybe set mouse pos should be done in EndFrame?) + ImRect rect_rel = WindowRectRelToAbs(window, window->NavRectRel[g.NavLayer]); + if (window->LastFrameActive != g.FrameCount && (window->ScrollTarget.x != FLT_MAX || window->ScrollTarget.y != FLT_MAX)) + { + ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window); + rect_rel.Translate(window->Scroll - next_scroll); + } + ImVec2 pos = ImVec2(rect_rel.Min.x + ImMin(g.Style.FramePadding.x * 4, rect_rel.GetWidth()), rect_rel.Max.y - ImMin(g.Style.FramePadding.y, rect_rel.GetHeight())); + ImGuiViewport* viewport = GetMainViewport(); + return ImFloor(ImClamp(pos, viewport->Pos, viewport->Pos + viewport->Size)); // ImFloor() is important because non-integer mouse position application in backend might be lossy and result in undesirable non-zero delta. } } +const char* ImGui::GetNavInputName(ImGuiNavInput n) +{ + static const char* names[] = + { + "Activate", "Cancel", "Input", "Menu", "DpadLeft", "DpadRight", "DpadUp", "DpadDown", "LStickLeft", "LStickRight", "LStickUp", "LStickDown", + "FocusPrev", "FocusNext", "TweakSlow", "TweakFast", "KeyLeft", "KeyRight", "KeyUp", "KeyDown" + }; + IM_ASSERT(IM_ARRAYSIZE(names) == ImGuiNavInput_COUNT); + IM_ASSERT(n >= 0 && n < ImGuiNavInput_COUNT); + return names[n]; +} + float ImGui::GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode) { ImGuiContext& g = *GImGui; @@ -8775,6 +10012,8 @@ float ImGui::GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode) ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor, float fast_factor) { ImVec2 delta(0.0f, 0.0f); + if (dir_sources & ImGuiNavDirSourceFlags_RawKeyboard) + delta += ImVec2((float)IsKeyDown(ImGuiKey_RightArrow) - (float)IsKeyDown(ImGuiKey_LeftArrow), (float)IsKeyDown(ImGuiKey_DownArrow) - (float)IsKeyDown(ImGuiKey_UpArrow)); if (dir_sources & ImGuiNavDirSourceFlags_Keyboard) delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight_, mode) - GetNavInputAmount(ImGuiNavInput_KeyLeft_, mode), GetNavInputAmount(ImGuiNavInput_KeyDown_, mode) - GetNavInputAmount(ImGuiNavInput_KeyUp_, mode)); if (dir_sources & ImGuiNavDirSourceFlags_PadDPad) @@ -8794,27 +10033,40 @@ static void ImGui::NavUpdate() ImGuiIO& io = g.IO; io.WantSetMousePos = false; - g.NavWrapRequestWindow = NULL; - g.NavWrapRequestFlags = ImGuiNavMoveFlags_None; -#if 0 - if (g.NavScoringCount > 0) IMGUI_DEBUG_LOG("NavScoringCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.FrameCount, g.NavScoringCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest); -#endif + //if (g.NavScoringDebugCount > 0) IMGUI_DEBUG_LOG("NavScoringDebugCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.NavScoringDebugCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest); + // Update Gamepad->Nav inputs mapping // Set input source as Gamepad when buttons are pressed (as some features differs when used with Gamepad vs Keyboard) - // (do it before we map Keyboard input!) - bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; - bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; - if (nav_gamepad_active && g.NavInputSource != ImGuiInputSource_NavGamepad) - { - if (io.NavInputs[ImGuiNavInput_Activate] > 0.0f || io.NavInputs[ImGuiNavInput_Input] > 0.0f || io.NavInputs[ImGuiNavInput_Cancel] > 0.0f || io.NavInputs[ImGuiNavInput_Menu] > 0.0f - || io.NavInputs[ImGuiNavInput_DpadLeft] > 0.0f || io.NavInputs[ImGuiNavInput_DpadRight] > 0.0f || io.NavInputs[ImGuiNavInput_DpadUp] > 0.0f || io.NavInputs[ImGuiNavInput_DpadDown] > 0.0f) - g.NavInputSource = ImGuiInputSource_NavGamepad; + const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; + if (nav_gamepad_active && g.IO.BackendUsingLegacyNavInputArray == false) + { + for (int n = 0; n < ImGuiNavInput_COUNT; n++) + IM_ASSERT(io.NavInputs[n] == 0.0f && "Backend needs to either only use io.AddKeyEvent()/io.AddKeyAnalogEvent(), either only fill legacy io.NavInputs[]. Not both!"); + #define NAV_MAP_KEY(_KEY, _NAV_INPUT, _ACTIVATE_NAV) do { io.NavInputs[_NAV_INPUT] = io.KeysData[_KEY - ImGuiKey_KeysData_OFFSET].AnalogValue; if (_ACTIVATE_NAV && io.NavInputs[_NAV_INPUT] > 0.0f) { g.NavInputSource = ImGuiInputSource_Gamepad; } } while (0) + NAV_MAP_KEY(ImGuiKey_GamepadFaceDown, ImGuiNavInput_Activate, true); + NAV_MAP_KEY(ImGuiKey_GamepadFaceRight, ImGuiNavInput_Cancel, true); + NAV_MAP_KEY(ImGuiKey_GamepadFaceLeft, ImGuiNavInput_Menu, true); + NAV_MAP_KEY(ImGuiKey_GamepadFaceUp, ImGuiNavInput_Input, true); + NAV_MAP_KEY(ImGuiKey_GamepadDpadLeft, ImGuiNavInput_DpadLeft, true); + NAV_MAP_KEY(ImGuiKey_GamepadDpadRight, ImGuiNavInput_DpadRight, true); + NAV_MAP_KEY(ImGuiKey_GamepadDpadUp, ImGuiNavInput_DpadUp, true); + NAV_MAP_KEY(ImGuiKey_GamepadDpadDown, ImGuiNavInput_DpadDown, true); + NAV_MAP_KEY(ImGuiKey_GamepadL1, ImGuiNavInput_FocusPrev, false); + NAV_MAP_KEY(ImGuiKey_GamepadR1, ImGuiNavInput_FocusNext, false); + NAV_MAP_KEY(ImGuiKey_GamepadL1, ImGuiNavInput_TweakSlow, false); + NAV_MAP_KEY(ImGuiKey_GamepadR1, ImGuiNavInput_TweakFast, false); + NAV_MAP_KEY(ImGuiKey_GamepadLStickLeft, ImGuiNavInput_LStickLeft, false); + NAV_MAP_KEY(ImGuiKey_GamepadLStickRight, ImGuiNavInput_LStickRight, false); + NAV_MAP_KEY(ImGuiKey_GamepadLStickUp, ImGuiNavInput_LStickUp, false); + NAV_MAP_KEY(ImGuiKey_GamepadLStickDown, ImGuiNavInput_LStickDown, false); + #undef NAV_MAP_KEY } // Update Keyboard->Nav inputs mapping + const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; if (nav_keyboard_active) { - #define NAV_MAP_KEY(_KEY, _NAV_INPUT) do { if (IsKeyDown(io.KeyMap[_KEY])) { io.NavInputs[_NAV_INPUT] = 1.0f; g.NavInputSource = ImGuiInputSource_NavKeyboard; } } while (0) + #define NAV_MAP_KEY(_KEY, _NAV_INPUT) do { if (IsKeyDown(_KEY)) { io.NavInputs[_NAV_INPUT] = 1.0f; g.NavInputSource = ImGuiInputSource_Keyboard; } } while (0) NAV_MAP_KEY(ImGuiKey_Space, ImGuiNavInput_Activate ); NAV_MAP_KEY(ImGuiKey_Enter, ImGuiNavInput_Input ); NAV_MAP_KEY(ImGuiKey_Escape, ImGuiNavInput_Cancel ); @@ -8826,8 +10078,6 @@ static void ImGui::NavUpdate() io.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f; if (io.KeyShift) io.NavInputs[ImGuiNavInput_TweakFast] = 1.0f; - if (io.KeyAlt && !io.KeyCtrl) // AltGR is Alt+Ctrl, also even on keyboards without AltGR we don't want Alt+Ctrl to open menu. - io.NavInputs[ImGuiNavInput_KeyMenu_] = 1.0f; #undef NAV_MAP_KEY } memcpy(io.NavInputsDownDurationPrev, io.NavInputsDownDuration, sizeof(io.NavInputsDownDuration)); @@ -8835,172 +10085,90 @@ static void ImGui::NavUpdate() io.NavInputsDownDuration[i] = (io.NavInputs[i] > 0.0f) ? (io.NavInputsDownDuration[i] < 0.0f ? 0.0f : io.NavInputsDownDuration[i] + io.DeltaTime) : -1.0f; // Process navigation init request (select first/default focus) - if (g.NavInitResultId != 0 && (!g.NavDisableHighlight || g.NavInitRequestFromMove)) - NavUpdateInitResult(); + if (g.NavInitResultId != 0) + NavInitRequestApplyResult(); g.NavInitRequest = false; g.NavInitRequestFromMove = false; g.NavInitResultId = 0; g.NavJustMovedToId = 0; // Process navigation move request - if (g.NavMoveRequest) - NavUpdateMoveResult(); - - // When a forwarded move request failed, we restore the highlight that we disabled during the forward frame - if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive) - { - IM_ASSERT(g.NavMoveRequest); - if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0) - g.NavDisableHighlight = false; - g.NavMoveRequestForward = ImGuiNavForward_None; - } + if (g.NavMoveSubmitted) + NavMoveRequestApplyResult(); + g.NavTabbingCounter = 0; + g.NavMoveSubmitted = g.NavMoveScoringItems = false; - // Apply application mouse position movement, after we had a chance to process move request result. + // Schedule mouse position update (will be done at the bottom of this function, after 1) processing all move requests and 2) updating scrolling) + bool set_mouse_pos = false; if (g.NavMousePosDirty && g.NavIdIsAlive) - { - // Set mouse position given our knowledge of the navigated item position from last frame - if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos)) - { - if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow) - { - io.MousePos = io.MousePosPrev = NavCalcPreferredRefPos(); - io.WantSetMousePos = true; - } - } - g.NavMousePosDirty = false; - } - g.NavIdIsAlive = false; - g.NavJustTabbedId = 0; - IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1); - - // Store our return window (for returning from Layer 1 to Layer 0) and clear it as soon as we step back in our own Layer 0 - if (g.NavWindow) - NavSaveLastChildNavWindowIntoParent(g.NavWindow); - if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == ImGuiNavLayer_Main) - g.NavWindow->NavLastChildNavWindow = NULL; - - // Update CTRL+TAB and Windowing features (hold Square to move/resize/etc.) - NavUpdateWindowing(); - - // Set output flags for user application - io.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs); - io.NavVisible = (io.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL); - - // Process NavCancel input (to close a popup, get back to parent, clear focus) - if (IsNavInputTest(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed)) - { - IMGUI_DEBUG_LOG_NAV("[nav] ImGuiNavInput_Cancel\n"); - if (g.ActiveId != 0) - { - if (!IsActiveIdUsingNavInput(ImGuiNavInput_Cancel)) - ClearActiveID(); - } - else if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow) && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow) - { - // Exit child window - ImGuiWindow* child_window = g.NavWindow; - ImGuiWindow* parent_window = g.NavWindow->ParentWindow; - IM_ASSERT(child_window->ChildId != 0); - FocusWindow(parent_window); - SetNavID(child_window->ChildId, 0, 0); - // Reassigning with same value, we're being explicit here. - g.NavIdIsAlive = false; // -V1048 - if (g.NavDisableMouseHover) - g.NavMousePosDirty = true; - } - else if (g.OpenPopupStack.Size > 0) - { - // Close open popup/menu - if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal)) - ClosePopupToLevel(g.OpenPopupStack.Size - 1, true); - } - else if (g.NavLayer != ImGuiNavLayer_Main) - { - // Leave the "menu" layer - NavRestoreLayer(ImGuiNavLayer_Main); - } - else - { - // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were - if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow))) - g.NavWindow->NavLastIds[0] = 0; - g.NavId = g.NavFocusScopeId = 0; - } - } + if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow) + set_mouse_pos = true; + g.NavMousePosDirty = false; + IM_ASSERT(g.NavLayer == ImGuiNavLayer_Main || g.NavLayer == ImGuiNavLayer_Menu); + + // Store our return window (for returning from Menu Layer to Main Layer) and clear it as soon as we step back in our own Layer 0 + if (g.NavWindow) + NavSaveLastChildNavWindowIntoParent(g.NavWindow); + if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == ImGuiNavLayer_Main) + g.NavWindow->NavLastChildNavWindow = NULL; + + // Update CTRL+TAB and Windowing features (hold Square to move/resize/etc.) + NavUpdateWindowing(); + + // Set output flags for user application + io.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs); + io.NavVisible = (io.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL); + + // Process NavCancel input (to close a popup, get back to parent, clear focus) + NavUpdateCancelRequest(); // Process manual activation request - g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = 0; + g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavActivateInputId = 0; + g.NavActivateFlags = ImGuiActivateFlags_None; if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) { bool activate_down = IsNavInputDown(ImGuiNavInput_Activate); + bool input_down = IsNavInputDown(ImGuiNavInput_Input); bool activate_pressed = activate_down && IsNavInputTest(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed); + bool input_pressed = input_down && IsNavInputTest(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed); if (g.ActiveId == 0 && activate_pressed) + { g.NavActivateId = g.NavId; + g.NavActivateFlags = ImGuiActivateFlags_PreferTweak; + } + if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && input_pressed) + { + g.NavActivateInputId = g.NavId; + g.NavActivateFlags = ImGuiActivateFlags_PreferInput; + } if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_down) g.NavActivateDownId = g.NavId; if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_pressed) g.NavActivatePressedId = g.NavId; - if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && IsNavInputTest(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed)) - g.NavInputId = g.NavId; } if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) g.NavDisableHighlight = true; if (g.NavActivateId != 0) IM_ASSERT(g.NavActivateDownId == g.NavActivateId); - g.NavMoveRequest = false; // Process programmatic activation request + // FIXME-NAV: Those should eventually be queued (unlike focus they don't cancel each others) if (g.NavNextActivateId != 0) - g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = g.NavNextActivateId; - g.NavNextActivateId = 0; - - // Initiate directional inputs request - if (g.NavMoveRequestForward == ImGuiNavForward_None) - { - g.NavMoveDir = ImGuiDir_None; - g.NavMoveRequestFlags = ImGuiNavMoveFlags_None; - if (g.NavWindow && !g.NavWindowingTarget && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) - { - const ImGuiInputReadMode read_mode = ImGuiInputReadMode_Repeat; - if (!IsActiveIdUsingNavDir(ImGuiDir_Left) && (IsNavInputTest(ImGuiNavInput_DpadLeft, read_mode) || IsNavInputTest(ImGuiNavInput_KeyLeft_, read_mode))) { g.NavMoveDir = ImGuiDir_Left; } - if (!IsActiveIdUsingNavDir(ImGuiDir_Right) && (IsNavInputTest(ImGuiNavInput_DpadRight, read_mode) || IsNavInputTest(ImGuiNavInput_KeyRight_, read_mode))) { g.NavMoveDir = ImGuiDir_Right; } - if (!IsActiveIdUsingNavDir(ImGuiDir_Up) && (IsNavInputTest(ImGuiNavInput_DpadUp, read_mode) || IsNavInputTest(ImGuiNavInput_KeyUp_, read_mode))) { g.NavMoveDir = ImGuiDir_Up; } - if (!IsActiveIdUsingNavDir(ImGuiDir_Down) && (IsNavInputTest(ImGuiNavInput_DpadDown, read_mode) || IsNavInputTest(ImGuiNavInput_KeyDown_, read_mode))) { g.NavMoveDir = ImGuiDir_Down; } - } - g.NavMoveClipDir = g.NavMoveDir; - } - else { - // Forwarding previous request (which has been modified, e.g. wrap around menus rewrite the requests with a starting rectangle at the other side of the window) - // (Preserve g.NavMoveRequestFlags, g.NavMoveClipDir which were set by the NavMoveRequestForward() function) - IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None); - IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_ForwardQueued); - IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequestForward %d\n", g.NavMoveDir); - g.NavMoveRequestForward = ImGuiNavForward_ForwardActive; + if (g.NavNextActivateFlags & ImGuiActivateFlags_PreferInput) + g.NavActivateInputId = g.NavNextActivateId; + else + g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavNextActivateId; + g.NavActivateFlags = g.NavNextActivateFlags; } + g.NavNextActivateId = 0; - // Update PageUp/PageDown/Home/End scroll - // FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags_NavEnableKeyboard flag? - float nav_scoring_rect_offset_y = 0.0f; - if (nav_keyboard_active) - nav_scoring_rect_offset_y = NavUpdatePageUpPageDown(); - - // If we initiate a movement request and have no current NavId, we initiate a InitDefautRequest that will be used as a fallback if the direction fails to find a match - if (g.NavMoveDir != ImGuiDir_None) - { - g.NavMoveRequest = true; - g.NavMoveRequestKeyMods = io.KeyMods; - g.NavMoveDirLast = g.NavMoveDir; - } - if (g.NavMoveRequest && g.NavId == 0) - { - IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from move, window \"%s\", layer=%d\n", g.NavWindow->Name, g.NavLayer); - g.NavInitRequest = g.NavInitRequestFromMove = true; - // Reassigning with same value, we're being explicit here. - g.NavInitResultId = 0; // -V1048 - g.NavDisableHighlight = false; - } + // Process move requests + NavUpdateCreateMoveRequest(); + if (g.NavMoveDir == ImGuiDir_None) + NavUpdateCreateTabbingRequest(); NavUpdateAnyRequestFlag(); + g.NavIdIsAlive = false; // Scrolling if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget) @@ -9008,12 +10176,13 @@ static void ImGui::NavUpdate() // *Fallback* manual-scroll with Nav directional keys when window has no navigable item ImGuiWindow* window = g.NavWindow; const float scroll_speed = IM_ROUND(window->CalcFontSize() * 100 * io.DeltaTime); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported. - if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest) + const ImGuiDir move_dir = g.NavMoveDir; + if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavHasScroll && move_dir != ImGuiDir_None) { - if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) - SetScrollX(window, ImFloor(window->Scroll.x + ((g.NavMoveDir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed)); - if (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) - SetScrollY(window, ImFloor(window->Scroll.y + ((g.NavMoveDir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed)); + if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right) + SetScrollX(window, ImFloor(window->Scroll.x + ((move_dir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed)); + if (move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down) + SetScrollY(window, ImFloor(window->Scroll.y + ((move_dir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed)); } // *Normal* Manual scroll with NavScrollXXX keys @@ -9025,48 +10194,35 @@ static void ImGui::NavUpdate() SetScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed)); } - // Reset search results - g.NavMoveResultLocal.Clear(); - g.NavMoveResultLocalVisibleSet.Clear(); - g.NavMoveResultOther.Clear(); + // Always prioritize mouse highlight if navigation is disabled + if (!nav_keyboard_active && !nav_gamepad_active) + { + g.NavDisableHighlight = true; + g.NavDisableMouseHover = set_mouse_pos = false; + } - // When using gamepad, we project the reference nav bounding box into window visible area. - // This is to allow resuming navigation inside the visible area after doing a large amount of scrolling, since with gamepad every movements are relative - // (can't focus a visible object like we can with the mouse). - if (g.NavMoveRequest && g.NavInputSource == ImGuiInputSource_NavGamepad && g.NavLayer == ImGuiNavLayer_Main) + // Update mouse position if requested + // (This will take into account the possibility that a Scroll was queued in the window to offset our absolute mouse position before scroll has been applied) + if (set_mouse_pos && (io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos)) { - ImGuiWindow* window = g.NavWindow; - ImRect window_rect_rel(window->InnerRect.Min - window->Pos - ImVec2(1, 1), window->InnerRect.Max - window->Pos + ImVec2(1, 1)); - if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer])) - { - IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: clamp NavRectRel\n"); - float pad = window->CalcFontSize() * 0.5f; - window_rect_rel.Expand(ImVec2(-ImMin(window_rect_rel.GetWidth(), pad), -ImMin(window_rect_rel.GetHeight(), pad))); // Terrible approximation for the intent of starting navigation from first fully visible item - window->NavRectRel[g.NavLayer].ClipWithFull(window_rect_rel); - g.NavId = g.NavFocusScopeId = 0; - } + io.MousePos = io.MousePosPrev = NavCalcPreferredRefPos(); + io.WantSetMousePos = true; + //IMGUI_DEBUG_LOG("SetMousePos: (%.1f,%.1f)\n", io.MousePos.x, io.MousePos.y); } - // For scoring we use a single segment on the left side our current item bounding box (not touching the edge to avoid box overlap with zero-spaced items) - ImRect nav_rect_rel = g.NavWindow ? g.NavWindow->NavRectRel[g.NavLayer] : ImRect(0, 0, 0, 0); - g.NavScoringRect = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : GetViewportRect(); - g.NavScoringRect.TranslateY(nav_scoring_rect_offset_y); - g.NavScoringRect.Min.x = ImMin(g.NavScoringRect.Min.x + 1.0f, g.NavScoringRect.Max.x); - g.NavScoringRect.Max.x = g.NavScoringRect.Min.x; - IM_ASSERT(!g.NavScoringRect.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous ImFabs() calls in NavScoreItem(). - //GetForegroundDrawList()->AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG] - g.NavScoringCount = 0; + // [DEBUG] + g.NavScoringDebugCount = 0; #if IMGUI_DEBUG_NAV_RECTS if (g.NavWindow) { ImDrawList* draw_list = GetForegroundDrawList(g.NavWindow); - if (1) { for (int layer = 0; layer < 2; layer++) draw_list->AddRect(g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Min, g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Max, IM_COL32(255,200,0,255)); } // [DEBUG] + if (1) { for (int layer = 0; layer < 2; layer++) { ImRect r = WindowRectRelToAbs(g.NavWindow, g.NavWindow->NavRectRel[layer]); draw_list->AddRect(r.Min, r.Max, IM_COL32(255,200,0,255)); } } // [DEBUG] if (1) { ImU32 col = (!g.NavWindow->Hidden) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredRefPos(); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); draw_list->AddCircleFilled(p, 3.0f, col); draw_list->AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); } } #endif } -static void ImGui::NavUpdateInitResult() +void ImGui::NavInitRequestApplyResult() { // In very rare cases g.NavWindow may be null (e.g. clearing focus after requesting an init request, which does happen when releasing Alt while clicking on void) ImGuiContext& g = *GImGui; @@ -9074,36 +10230,178 @@ static void ImGui::NavUpdateInitResult() return; // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called) + // FIXME-NAV: On _NavFlattened windows, g.NavWindow will only be updated during subsequent frame. Not a problem currently. IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", g.NavInitResultId, g.NavLayer, g.NavWindow->Name); + SetNavID(g.NavInitResultId, g.NavLayer, 0, g.NavInitResultRectRel); + g.NavIdIsAlive = true; // Mark as alive from previous frame as we got a result if (g.NavInitRequestFromMove) - SetNavIDWithRectRel(g.NavInitResultId, g.NavLayer, 0, g.NavInitResultRectRel); - else - SetNavID(g.NavInitResultId, g.NavLayer, 0); - g.NavWindow->NavRectRel[g.NavLayer] = g.NavInitResultRectRel; + NavRestoreHighlightAfterMove(); } -// Apply result from previous frame navigation directional move request -static void ImGui::NavUpdateMoveResult() +void ImGui::NavUpdateCreateMoveRequest() { ImGuiContext& g = *GImGui; - if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0) + ImGuiIO& io = g.IO; + ImGuiWindow* window = g.NavWindow; + + if (g.NavMoveForwardToNextFrame && window != NULL) + { + // Forwarding previous request (which has been modified, e.g. wrap around menus rewrite the requests with a starting rectangle at the other side of the window) + // (preserve most state, which were already set by the NavMoveRequestForward() function) + IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None); + IM_ASSERT(g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded); + IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequestForward %d\n", g.NavMoveDir); + } + else { - // In a situation when there is no results but NavId != 0, re-enable the Navigation highlight (because g.NavId is not considered as a possible result) - if (g.NavId != 0) + // Initiate directional inputs request + g.NavMoveDir = ImGuiDir_None; + g.NavMoveFlags = ImGuiNavMoveFlags_None; + g.NavMoveScrollFlags = ImGuiScrollFlags_None; + if (window && !g.NavWindowingTarget && !(window->Flags & ImGuiWindowFlags_NoNavInputs)) { - g.NavDisableHighlight = false; - g.NavDisableMouseHover = true; + const ImGuiInputReadMode read_mode = ImGuiInputReadMode_Repeat; + if (!IsActiveIdUsingNavDir(ImGuiDir_Left) && (IsNavInputTest(ImGuiNavInput_DpadLeft, read_mode) || IsNavInputTest(ImGuiNavInput_KeyLeft_, read_mode))) { g.NavMoveDir = ImGuiDir_Left; } + if (!IsActiveIdUsingNavDir(ImGuiDir_Right) && (IsNavInputTest(ImGuiNavInput_DpadRight, read_mode) || IsNavInputTest(ImGuiNavInput_KeyRight_, read_mode))) { g.NavMoveDir = ImGuiDir_Right; } + if (!IsActiveIdUsingNavDir(ImGuiDir_Up) && (IsNavInputTest(ImGuiNavInput_DpadUp, read_mode) || IsNavInputTest(ImGuiNavInput_KeyUp_, read_mode))) { g.NavMoveDir = ImGuiDir_Up; } + if (!IsActiveIdUsingNavDir(ImGuiDir_Down) && (IsNavInputTest(ImGuiNavInput_DpadDown, read_mode) || IsNavInputTest(ImGuiNavInput_KeyDown_, read_mode))) { g.NavMoveDir = ImGuiDir_Down; } } - return; + g.NavMoveClipDir = g.NavMoveDir; + g.NavScoringNoClipRect = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX); + } + + // Update PageUp/PageDown/Home/End scroll + // FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags_NavEnableKeyboard flag? + const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; + float scoring_rect_offset_y = 0.0f; + if (window && g.NavMoveDir == ImGuiDir_None && nav_keyboard_active) + scoring_rect_offset_y = NavUpdatePageUpPageDown(); + if (scoring_rect_offset_y != 0.0f) + { + g.NavScoringNoClipRect = window->InnerRect; + g.NavScoringNoClipRect.TranslateY(scoring_rect_offset_y); + } + + // [DEBUG] Always send a request +#if IMGUI_DEBUG_NAV_SCORING + if (io.KeyCtrl && IsKeyPressed(ImGuiKey_C)) + g.NavMoveDirForDebug = (ImGuiDir)((g.NavMoveDirForDebug + 1) & 3); + if (io.KeyCtrl && g.NavMoveDir == ImGuiDir_None) + { + g.NavMoveDir = g.NavMoveDirForDebug; + g.NavMoveFlags |= ImGuiNavMoveFlags_DebugNoResult; + } +#endif + + // Submit + g.NavMoveForwardToNextFrame = false; + if (g.NavMoveDir != ImGuiDir_None) + NavMoveRequestSubmit(g.NavMoveDir, g.NavMoveClipDir, g.NavMoveFlags, g.NavMoveScrollFlags); + + // Moving with no reference triggers a init request (will be used as a fallback if the direction fails to find a match) + if (g.NavMoveSubmitted && g.NavId == 0) + { + IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from move, window \"%s\", layer=%d\n", g.NavWindow->Name, g.NavLayer); + g.NavInitRequest = g.NavInitRequestFromMove = true; + g.NavInitResultId = 0; + g.NavDisableHighlight = false; + } + + // When using gamepad, we project the reference nav bounding box into window visible area. + // This is to allow resuming navigation inside the visible area after doing a large amount of scrolling, since with gamepad every movements are relative + // (can't focus a visible object like we can with the mouse). + if (g.NavMoveSubmitted && g.NavInputSource == ImGuiInputSource_Gamepad && g.NavLayer == ImGuiNavLayer_Main && window != NULL)// && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded)) + { + bool clamp_x = (g.NavMoveFlags & (ImGuiNavMoveFlags_LoopX | ImGuiNavMoveFlags_WrapX)) == 0; + bool clamp_y = (g.NavMoveFlags & (ImGuiNavMoveFlags_LoopY | ImGuiNavMoveFlags_WrapY)) == 0; + ImRect inner_rect_rel = WindowRectAbsToRel(window, ImRect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1))); + if ((clamp_x || clamp_y) && !inner_rect_rel.Contains(window->NavRectRel[g.NavLayer])) + { + IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: clamp NavRectRel for gamepad move\n"); + float pad_x = ImMin(inner_rect_rel.GetWidth(), window->CalcFontSize() * 0.5f); + float pad_y = ImMin(inner_rect_rel.GetHeight(), window->CalcFontSize() * 0.5f); // Terrible approximation for the intent of starting navigation from first fully visible item + inner_rect_rel.Min.x = clamp_x ? (inner_rect_rel.Min.x + pad_x) : -FLT_MAX; + inner_rect_rel.Max.x = clamp_x ? (inner_rect_rel.Max.x - pad_x) : +FLT_MAX; + inner_rect_rel.Min.y = clamp_y ? (inner_rect_rel.Min.y + pad_y) : -FLT_MAX; + inner_rect_rel.Max.y = clamp_y ? (inner_rect_rel.Max.y - pad_y) : +FLT_MAX; + window->NavRectRel[g.NavLayer].ClipWithFull(inner_rect_rel); + g.NavId = g.NavFocusScopeId = 0; + } + } + + // For scoring we use a single segment on the left side our current item bounding box (not touching the edge to avoid box overlap with zero-spaced items) + ImRect scoring_rect; + if (window != NULL) + { + ImRect nav_rect_rel = !window->NavRectRel[g.NavLayer].IsInverted() ? window->NavRectRel[g.NavLayer] : ImRect(0, 0, 0, 0); + scoring_rect = WindowRectRelToAbs(window, nav_rect_rel); + scoring_rect.TranslateY(scoring_rect_offset_y); + scoring_rect.Min.x = ImMin(scoring_rect.Min.x + 1.0f, scoring_rect.Max.x); + scoring_rect.Max.x = scoring_rect.Min.x; + IM_ASSERT(!scoring_rect.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous ImFabs() calls in NavScoreItem(). + //GetForegroundDrawList()->AddRect(scoring_rect.Min, scoring_rect.Max, IM_COL32(255,200,0,255)); // [DEBUG] + //if (!g.NavScoringNoClipRect.IsInverted()) { GetForegroundDrawList()->AddRect(g.NavScoringNoClipRect.Min, g.NavScoringNoClipRect.Max, IM_COL32(255, 200, 0, 255)); } // [DEBUG] } + g.NavScoringRect = scoring_rect; + g.NavScoringNoClipRect.Add(scoring_rect); +} + +void ImGui::NavUpdateCreateTabbingRequest() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.NavWindow; + IM_ASSERT(g.NavMoveDir == ImGuiDir_None); + if (window == NULL || g.NavWindowingTarget != NULL || (window->Flags & ImGuiWindowFlags_NoNavInputs)) + return; + + const bool tab_pressed = IsKeyPressed(ImGuiKey_Tab, true) && !IsActiveIdUsingKey(ImGuiKey_Tab) && !g.IO.KeyCtrl && !g.IO.KeyAlt; + if (!tab_pressed) + return; + + // Initiate tabbing request + // (this is ALWAYS ENABLED, regardless of ImGuiConfigFlags_NavEnableKeyboard flag!) + // Initially this was designed to use counters and modulo arithmetic, but that could not work with unsubmitted items (list clipper). Instead we use a strategy close to other move requests. + // See NavProcessItemForTabbingRequest() for a description of the various forward/backward tabbing cases with and without wrapping. + //// FIXME: We use (g.ActiveId == 0) but (g.NavDisableHighlight == false) might be righter once we can tab through anything + g.NavTabbingDir = g.IO.KeyShift ? -1 : (g.ActiveId == 0) ? 0 : +1; + ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY; + ImGuiDir clip_dir = (g.NavTabbingDir < 0) ? ImGuiDir_Up : ImGuiDir_Down; + NavMoveRequestSubmit(ImGuiDir_None, clip_dir, ImGuiNavMoveFlags_Tabbing, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable. + g.NavTabbingResultFirst.Clear(); + g.NavTabbingCounter = -1; +} + +// Apply result from previous frame navigation directional move request. Always called from NavUpdate() +void ImGui::NavMoveRequestApplyResult() +{ + ImGuiContext& g = *GImGui; +#if IMGUI_DEBUG_NAV_SCORING + if (g.NavMoveFlags & ImGuiNavMoveFlags_DebugNoResult) // [DEBUG] Scoring all items in NavWindow at all times + return; +#endif // Select which result to use - ImGuiNavMoveResult* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; + ImGuiNavItemData* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : (g.NavMoveResultOther.ID != 0) ? &g.NavMoveResultOther : NULL; + + // Tabbing forward wrap + if (g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) + if ((g.NavTabbingCounter == 1 || g.NavTabbingDir == 0) && g.NavTabbingResultFirst.ID) + result = &g.NavTabbingResultFirst; + + // In a situation when there is no results but NavId != 0, re-enable the Navigation highlight (because g.NavId is not considered as a possible result) + if (result == NULL) + { + if (g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) + g.NavMoveFlags |= ImGuiNavMoveFlags_DontSetNavHighlight; + if (g.NavId != 0 && (g.NavMoveFlags & ImGuiNavMoveFlags_DontSetNavHighlight) == 0) + NavRestoreHighlightAfterMove(); + return; + } // PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page. - if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) - if (g.NavMoveResultLocalVisibleSet.ID != 0 && g.NavMoveResultLocalVisibleSet.ID != g.NavId) - result = &g.NavMoveResultLocalVisibleSet; + if (g.NavMoveFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) + if (g.NavMoveResultLocalVisible.ID != 0 && g.NavMoveResultLocalVisible.ID != g.NavId) + result = &g.NavMoveResultLocalVisible; // Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules. if (result != &g.NavMoveResultOther && g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow) @@ -9114,107 +10412,176 @@ static void ImGui::NavUpdateMoveResult() // Scroll to keep newly navigated item fully into view. if (g.NavLayer == ImGuiNavLayer_Main) { - ImVec2 delta_scroll; - if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_ScrollToEdge) + if (g.NavMoveFlags & ImGuiNavMoveFlags_ScrollToEdgeY) { + // FIXME: Should remove this float scroll_target = (g.NavMoveDir == ImGuiDir_Up) ? result->Window->ScrollMax.y : 0.0f; - delta_scroll.y = result->Window->Scroll.y - scroll_target; SetScrollY(result->Window, scroll_target); } else { - ImRect rect_abs = ImRect(result->RectRel.Min + result->Window->Pos, result->RectRel.Max + result->Window->Pos); - delta_scroll = ScrollToBringRectIntoView(result->Window, rect_abs); + ImRect rect_abs = WindowRectRelToAbs(result->Window, result->RectRel); + ScrollToRectEx(result->Window, rect_abs, g.NavMoveScrollFlags); } - - // Offset our result position so mouse position can be applied immediately after in NavUpdate() - result->RectRel.TranslateX(-delta_scroll.x); - result->RectRel.TranslateY(-delta_scroll.y); } - ClearActiveID(); g.NavWindow = result->Window; + if (g.ActiveId != result->ID) + ClearActiveID(); if (g.NavId != result->ID) { // Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId) g.NavJustMovedToId = result->ID; g.NavJustMovedToFocusScopeId = result->FocusScopeId; - g.NavJustMovedToKeyMods = g.NavMoveRequestKeyMods; + g.NavJustMovedToKeyMods = g.NavMoveKeyMods; } + + // Focus IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", result->ID, g.NavLayer, g.NavWindow->Name); - SetNavIDWithRectRel(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel); + SetNavID(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel); + + // Tabbing: Activates Inputable or Focus non-Inputable + if ((g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) && (result->InFlags & ImGuiItemFlags_Inputable)) + { + g.NavNextActivateId = result->ID; + g.NavNextActivateFlags = ImGuiActivateFlags_PreferInput | ImGuiActivateFlags_TryToPreserveState; + g.NavMoveFlags |= ImGuiNavMoveFlags_DontSetNavHighlight; + } + + // Activate + if (g.NavMoveFlags & ImGuiNavMoveFlags_Activate) + { + g.NavNextActivateId = result->ID; + g.NavNextActivateFlags = ImGuiActivateFlags_None; + } + + // Enable nav highlight + if ((g.NavMoveFlags & ImGuiNavMoveFlags_DontSetNavHighlight) == 0) + NavRestoreHighlightAfterMove(); +} + +// Process NavCancel input (to close a popup, get back to parent, clear focus) +// FIXME: In order to support e.g. Escape to clear a selection we'll need: +// - either to store the equivalent of ActiveIdUsingKeyInputMask for a FocusScope and test for it. +// - either to move most/all of those tests to the epilogue/end functions of the scope they are dealing with (e.g. exit child window in EndChild()) or in EndFrame(), to allow an earlier intercept +static void ImGui::NavUpdateCancelRequest() +{ + ImGuiContext& g = *GImGui; + if (!IsNavInputTest(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed)) + return; + + IMGUI_DEBUG_LOG_NAV("[nav] ImGuiNavInput_Cancel\n"); + if (g.ActiveId != 0) + { + if (!IsActiveIdUsingNavInput(ImGuiNavInput_Cancel)) + ClearActiveID(); + } + else if (g.NavLayer != ImGuiNavLayer_Main) + { + // Leave the "menu" layer + NavRestoreLayer(ImGuiNavLayer_Main); + NavRestoreHighlightAfterMove(); + } + else if (g.NavWindow && g.NavWindow != g.NavWindow->RootWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow) + { + // Exit child window + ImGuiWindow* child_window = g.NavWindow; + ImGuiWindow* parent_window = g.NavWindow->ParentWindow; + IM_ASSERT(child_window->ChildId != 0); + ImRect child_rect = child_window->Rect(); + FocusWindow(parent_window); + SetNavID(child_window->ChildId, ImGuiNavLayer_Main, 0, WindowRectAbsToRel(parent_window, child_rect)); + NavRestoreHighlightAfterMove(); + } + else if (g.OpenPopupStack.Size > 0) + { + // Close open popup/menu + if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal)) + ClosePopupToLevel(g.OpenPopupStack.Size - 1, true); + } + else + { + // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were + if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow))) + g.NavWindow->NavLastIds[0] = 0; + g.NavId = g.NavFocusScopeId = 0; + } } // Handle PageUp/PageDown/Home/End keys +// Called from NavUpdateCreateMoveRequest() which will use our output to create a move request +// FIXME-NAV: This doesn't work properly with NavFlattened siblings as we use NavWindow rectangle for reference +// FIXME-NAV: how to get Home/End to aim at the beginning/end of a 2D grid? static float ImGui::NavUpdatePageUpPageDown() { ImGuiContext& g = *GImGui; - ImGuiIO& io = g.IO; - - if (g.NavMoveDir != ImGuiDir_None || g.NavWindow == NULL) + ImGuiWindow* window = g.NavWindow; + if ((window->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL) return 0.0f; - if ((g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL || g.NavLayer != ImGuiNavLayer_Main) + + const bool page_up_held = IsKeyDown(ImGuiKey_PageUp) && !IsActiveIdUsingKey(ImGuiKey_PageUp); + const bool page_down_held = IsKeyDown(ImGuiKey_PageDown) && !IsActiveIdUsingKey(ImGuiKey_PageDown); + const bool home_pressed = IsKeyPressed(ImGuiKey_Home) && !IsActiveIdUsingKey(ImGuiKey_Home); + const bool end_pressed = IsKeyPressed(ImGuiKey_End) && !IsActiveIdUsingKey(ImGuiKey_End); + if (page_up_held == page_down_held && home_pressed == end_pressed) // Proceed if either (not both) are pressed, otherwise early out return 0.0f; - ImGuiWindow* window = g.NavWindow; - const bool page_up_held = IsKeyDown(io.KeyMap[ImGuiKey_PageUp]) && !IsActiveIdUsingKey(ImGuiKey_PageUp); - const bool page_down_held = IsKeyDown(io.KeyMap[ImGuiKey_PageDown]) && !IsActiveIdUsingKey(ImGuiKey_PageDown); - const bool home_pressed = IsKeyPressed(io.KeyMap[ImGuiKey_Home]) && !IsActiveIdUsingKey(ImGuiKey_Home); - const bool end_pressed = IsKeyPressed(io.KeyMap[ImGuiKey_End]) && !IsActiveIdUsingKey(ImGuiKey_End); - if (page_up_held != page_down_held || home_pressed != end_pressed) // If either (not both) are pressed - { - if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll) - { - // Fallback manual-scroll when window has no navigable item - if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true)) - SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight()); - else if (IsKeyPressed(io.KeyMap[ImGuiKey_PageDown], true)) - SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight()); - else if (home_pressed) - SetScrollY(window, 0.0f); - else if (end_pressed) - SetScrollY(window, window->ScrollMax.y); + if (g.NavLayer != ImGuiNavLayer_Main) + NavRestoreLayer(ImGuiNavLayer_Main); + + if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavHasScroll) + { + // Fallback manual-scroll when window has no navigable item + if (IsKeyPressed(ImGuiKey_PageUp, true)) + SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight()); + else if (IsKeyPressed(ImGuiKey_PageDown, true)) + SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight()); + else if (home_pressed) + SetScrollY(window, 0.0f); + else if (end_pressed) + SetScrollY(window, window->ScrollMax.y); + } + else + { + ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer]; + const float page_offset_y = ImMax(0.0f, window->InnerRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight()); + float nav_scoring_rect_offset_y = 0.0f; + if (IsKeyPressed(ImGuiKey_PageUp, true)) + { + nav_scoring_rect_offset_y = -page_offset_y; + g.NavMoveDir = ImGuiDir_Down; // Because our scoring rect is offset up, we request the down direction (so we can always land on the last item) + g.NavMoveClipDir = ImGuiDir_Up; + g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet; } - else + else if (IsKeyPressed(ImGuiKey_PageDown, true)) { - ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer]; - const float page_offset_y = ImMax(0.0f, window->InnerRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight()); - float nav_scoring_rect_offset_y = 0.0f; - if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true)) - { - nav_scoring_rect_offset_y = -page_offset_y; - g.NavMoveDir = ImGuiDir_Down; // Because our scoring rect is offset up, we request the down direction (so we can always land on the last item) - g.NavMoveClipDir = ImGuiDir_Up; - g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet; - } - else if (IsKeyPressed(io.KeyMap[ImGuiKey_PageDown], true)) - { - nav_scoring_rect_offset_y = +page_offset_y; - g.NavMoveDir = ImGuiDir_Up; // Because our scoring rect is offset down, we request the up direction (so we can always land on the last item) - g.NavMoveClipDir = ImGuiDir_Down; - g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet; - } - else if (home_pressed) - { - // FIXME-NAV: handling of Home/End is assuming that the top/bottom most item will be visible with Scroll.y == 0/ScrollMax.y - // Scrolling will be handled via the ImGuiNavMoveFlags_ScrollToEdge flag, we don't scroll immediately to avoid scrolling happening before nav result. - // Preserve current horizontal position if we have any. - nav_rect_rel.Min.y = nav_rect_rel.Max.y = -window->Scroll.y; - if (nav_rect_rel.IsInverted()) - nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f; - g.NavMoveDir = ImGuiDir_Down; - g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge; - } - else if (end_pressed) - { - nav_rect_rel.Min.y = nav_rect_rel.Max.y = window->ScrollMax.y + window->SizeFull.y - window->Scroll.y; - if (nav_rect_rel.IsInverted()) - nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f; - g.NavMoveDir = ImGuiDir_Up; - g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge; - } - return nav_scoring_rect_offset_y; + nav_scoring_rect_offset_y = +page_offset_y; + g.NavMoveDir = ImGuiDir_Up; // Because our scoring rect is offset down, we request the up direction (so we can always land on the last item) + g.NavMoveClipDir = ImGuiDir_Down; + g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet; + } + else if (home_pressed) + { + // FIXME-NAV: handling of Home/End is assuming that the top/bottom most item will be visible with Scroll.y == 0/ScrollMax.y + // Scrolling will be handled via the ImGuiNavMoveFlags_ScrollToEdgeY flag, we don't scroll immediately to avoid scrolling happening before nav result. + // Preserve current horizontal position if we have any. + nav_rect_rel.Min.y = nav_rect_rel.Max.y = 0.0f; + if (nav_rect_rel.IsInverted()) + nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f; + g.NavMoveDir = ImGuiDir_Down; + g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdgeY; + // FIXME-NAV: MoveClipDir left to _None, intentional? + } + else if (end_pressed) + { + nav_rect_rel.Min.y = nav_rect_rel.Max.y = window->ContentSize.y; + if (nav_rect_rel.IsInverted()) + nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f; + g.NavMoveDir = ImGuiDir_Up; + g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdgeY; + // FIXME-NAV: MoveClipDir left to _None, intentional? } + return nav_scoring_rect_offset_y; } return 0.0f; } @@ -9228,66 +10595,76 @@ static void ImGui::NavEndFrame() NavUpdateWindowingOverlay(); // Perform wrap-around in menus - ImGuiWindow* window = g.NavWrapRequestWindow; - ImGuiNavMoveFlags move_flags = g.NavWrapRequestFlags; - if (window != NULL && g.NavWindow == window && NavMoveRequestButNoResultYet() && g.NavMoveRequestForward == ImGuiNavForward_None && g.NavLayer == ImGuiNavLayer_Main) - { - IM_ASSERT(move_flags != 0); // No points calling this with no wrapping - ImRect bb_rel = window->NavRectRel[0]; + // FIXME-NAV: Wrap may need to apply a weight bias on the other axis. e.g. 4x4 grid with 2 last items missing on last item won't handle LoopY/WrapY correctly. + // FIXME-NAV: Wrap (not Loop) support could be handled by the scoring function and then WrapX would function without an extra frame. + const ImGuiNavMoveFlags wanted_flags = ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX | ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY; + if (g.NavWindow && NavMoveRequestButNoResultYet() && (g.NavMoveFlags & wanted_flags) && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded) == 0) + NavUpdateCreateWrappingRequest(); +} + +static void ImGui::NavUpdateCreateWrappingRequest() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.NavWindow; - ImGuiDir clip_dir = g.NavMoveDir; - if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX))) + bool do_forward = false; + ImRect bb_rel = window->NavRectRel[g.NavLayer]; + ImGuiDir clip_dir = g.NavMoveDir; + const ImGuiNavMoveFlags move_flags = g.NavMoveFlags; + if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX))) + { + bb_rel.Min.x = bb_rel.Max.x = window->ContentSize.x + window->WindowPadding.x; + if (move_flags & ImGuiNavMoveFlags_WrapX) { - bb_rel.Min.x = bb_rel.Max.x = - ImMax(window->SizeFull.x, window->ContentSize.x + window->WindowPadding.x * 2.0f) - window->Scroll.x; - if (move_flags & ImGuiNavMoveFlags_WrapX) - { - bb_rel.TranslateY(-bb_rel.GetHeight()); - clip_dir = ImGuiDir_Up; - } - NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags); + bb_rel.TranslateY(-bb_rel.GetHeight()); // Previous row + clip_dir = ImGuiDir_Up; } - if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX))) + do_forward = true; + } + if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX))) + { + bb_rel.Min.x = bb_rel.Max.x = -window->WindowPadding.x; + if (move_flags & ImGuiNavMoveFlags_WrapX) { - bb_rel.Min.x = bb_rel.Max.x = -window->Scroll.x; - if (move_flags & ImGuiNavMoveFlags_WrapX) - { - bb_rel.TranslateY(+bb_rel.GetHeight()); - clip_dir = ImGuiDir_Down; - } - NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags); + bb_rel.TranslateY(+bb_rel.GetHeight()); // Next row + clip_dir = ImGuiDir_Down; } - if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY))) + do_forward = true; + } + if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY))) + { + bb_rel.Min.y = bb_rel.Max.y = window->ContentSize.y + window->WindowPadding.y; + if (move_flags & ImGuiNavMoveFlags_WrapY) { - bb_rel.Min.y = bb_rel.Max.y = - ImMax(window->SizeFull.y, window->ContentSize.y + window->WindowPadding.y * 2.0f) - window->Scroll.y; - if (move_flags & ImGuiNavMoveFlags_WrapY) - { - bb_rel.TranslateX(-bb_rel.GetWidth()); - clip_dir = ImGuiDir_Left; - } - NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags); + bb_rel.TranslateX(-bb_rel.GetWidth()); // Previous column + clip_dir = ImGuiDir_Left; } - if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY))) + do_forward = true; + } + if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY))) + { + bb_rel.Min.y = bb_rel.Max.y = -window->WindowPadding.y; + if (move_flags & ImGuiNavMoveFlags_WrapY) { - bb_rel.Min.y = bb_rel.Max.y = -window->Scroll.y; - if (move_flags & ImGuiNavMoveFlags_WrapY) - { - bb_rel.TranslateX(+bb_rel.GetWidth()); - clip_dir = ImGuiDir_Right; - } - NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags); + bb_rel.TranslateX(+bb_rel.GetWidth()); // Next column + clip_dir = ImGuiDir_Right; } + do_forward = true; } + if (!do_forward) + return; + window->NavRectRel[g.NavLayer] = bb_rel; + NavMoveRequestForward(g.NavMoveDir, clip_dir, move_flags, g.NavMoveScrollFlags); } -static int ImGui::FindWindowFocusIndex(ImGuiWindow* window) // FIXME-OPT O(N) +static int ImGui::FindWindowFocusIndex(ImGuiWindow* window) { ImGuiContext& g = *GImGui; - for (int i = g.WindowsFocusOrder.Size - 1; i >= 0; i--) - if (g.WindowsFocusOrder[i] == window) - return i; - return -1; + IM_UNUSED(g); + int order = window->FocusOrder; + IM_ASSERT(window->RootWindow == window); // No child window (not testing _ChildWindow because of docking) + IM_ASSERT(g.WindowsFocusOrder[order] == window); + return order; } static ImGuiWindow* FindWindowNavFocusable(int i_start, int i_stop, int dir) // FIXME-OPT O(N) @@ -9321,6 +10698,8 @@ static void NavUpdateWindowingHighlightWindow(int focus_change_dir) static void ImGui::NavUpdateWindowing() { ImGuiContext& g = *GImGui; + ImGuiIO& io = g.IO; + ImGuiWindow* apply_focus_window = NULL; bool apply_toggle_layer = false; @@ -9332,26 +10711,26 @@ static void ImGui::NavUpdateWindowing() // Fade out if (g.NavWindowingTargetAnim && g.NavWindowingTarget == NULL) { - g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha - g.IO.DeltaTime * 10.0f, 0.0f); + g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha - io.DeltaTime * 10.0f, 0.0f); if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f) g.NavWindowingTargetAnim = NULL; } - // Start CTRL-TAB or Square+L/R window selection - bool start_windowing_with_gamepad = allow_windowing && !g.NavWindowingTarget && IsNavInputTest(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed); - bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard); + // Start CTRL+Tab or Square+L/R window selection + const bool start_windowing_with_gamepad = allow_windowing && !g.NavWindowingTarget && IsNavInputTest(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed); + const bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && io.KeyCtrl && IsKeyPressed(ImGuiKey_Tab); if (start_windowing_with_gamepad || start_windowing_with_keyboard) if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1)) { - g.NavWindowingTarget = g.NavWindowingTargetAnim = window->RootWindow; // FIXME-DOCK: Will need to use RootWindowDockStop + g.NavWindowingTarget = g.NavWindowingTargetAnim = window->RootWindow; g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f; - g.NavWindowingToggleLayer = start_windowing_with_keyboard ? false : true; - g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_NavKeyboard : ImGuiInputSource_NavGamepad; + g.NavWindowingToggleLayer = start_windowing_with_gamepad ? true : false; // Gamepad starts toggling layer + g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_Keyboard : ImGuiInputSource_Gamepad; } // Gamepad update - g.NavWindowingTimer += g.IO.DeltaTime; - if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavGamepad) + g.NavWindowingTimer += io.DeltaTime; + if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_Gamepad) { // Highlight only appears after a brief time holding the button, so that a fast tap on PadMenu (to toggle NavLayer) doesn't add visual noise g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); @@ -9377,36 +10756,55 @@ static void ImGui::NavUpdateWindowing() } // Keyboard: Focus - if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavKeyboard) + if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_Keyboard) { // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // 1.0f - if (IsKeyPressedMap(ImGuiKey_Tab, true)) - NavUpdateWindowingHighlightWindow(g.IO.KeyShift ? +1 : -1); - if (!g.IO.KeyCtrl) + if (IsKeyPressed(ImGuiKey_Tab, true)) + NavUpdateWindowingHighlightWindow(io.KeyShift ? +1 : -1); + if (!io.KeyCtrl) apply_focus_window = g.NavWindowingTarget; } // Keyboard: Press and Release ALT to toggle menu layer - // FIXME: We lack an explicit IO variable for "is the imgui window focused", so compare mouse validity to detect the common case of backend clearing releases all keys on ALT-TAB - if (IsNavInputTest(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Pressed)) + // - Testing that only Alt is tested prevents Alt+Shift or AltGR from toggling menu layer. + // - AltGR is normally Alt+Ctrl but we can't reliably detect it (not all backends/systems/layout emit it as Alt+Ctrl). But even on keyboards without AltGR we don't want Alt+Ctrl to open menu anyway. + const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; + if (nav_keyboard_active && io.KeyMods == ImGuiKeyModFlags_Alt && (io.KeyModsPrev & ImGuiKeyModFlags_Alt) == 0) + { g.NavWindowingToggleLayer = true; - if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && g.NavWindowingToggleLayer && IsNavInputTest(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Released)) - if (IsMousePosValid(&g.IO.MousePos) == IsMousePosValid(&g.IO.MousePosPrev)) - apply_toggle_layer = true; + g.NavInputSource = ImGuiInputSource_Keyboard; + } + if (g.NavWindowingToggleLayer && g.NavInputSource == ImGuiInputSource_Keyboard) + { + // We cancel toggling nav layer when any text has been typed (generally while holding Alt). (See #370) + // We cancel toggling nav layer when other modifiers are pressed. (See #4439) + if (io.InputQueueCharacters.Size > 0 || io.KeyCtrl || io.KeyShift || io.KeySuper) + g.NavWindowingToggleLayer = false; + + // Apply layer toggle on release + // Important: we don't assume that Alt was previously held in order to handle loss of focus when backend calls io.AddFocusEvent(false) + // Important: as before version <18314 we lacked an explicit IO event for focus gain/loss, we also compare mouse validity to detect old backends clearing mouse pos on focus loss. + if (!(io.KeyMods & ImGuiKeyModFlags_Alt) && (io.KeyModsPrev & ImGuiKeyModFlags_Alt) && g.NavWindowingToggleLayer) + if (g.ActiveId == 0 || g.ActiveIdAllowOverlap) + if (IsMousePosValid(&io.MousePos) == IsMousePosValid(&io.MousePosPrev)) + apply_toggle_layer = true; + if (!io.KeyAlt) + g.NavWindowingToggleLayer = false; + } // Move window if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove)) { ImVec2 move_delta; - if (g.NavInputSource == ImGuiInputSource_NavKeyboard && !g.IO.KeyShift) - move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down); - if (g.NavInputSource == ImGuiInputSource_NavGamepad) + if (g.NavInputSource == ImGuiInputSource_Keyboard && !io.KeyShift) + move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_RawKeyboard, ImGuiInputReadMode_Down); + if (g.NavInputSource == ImGuiInputSource_Gamepad) move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down); if (move_delta.x != 0.0f || move_delta.y != 0.0f) { const float NAV_MOVE_SPEED = 800.0f; - const float move_speed = ImFloor(NAV_MOVE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y)); // FIXME: Doesn't handle variable framerate very well + const float move_speed = ImFloor(NAV_MOVE_SPEED * io.DeltaTime * ImMin(io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y)); // FIXME: Doesn't handle variable framerate very well ImGuiWindow* moving_window = g.NavWindowingTarget->RootWindow; SetWindowPos(moving_window, moving_window->Pos + move_delta * move_speed, ImGuiCond_Always); MarkIniSettingsDirty(moving_window); @@ -9418,16 +10816,21 @@ static void ImGui::NavUpdateWindowing() if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow)) { ClearActiveID(); - g.NavDisableHighlight = false; - g.NavDisableMouseHover = true; + NavRestoreHighlightAfterMove(); apply_focus_window = NavRestoreLastChildNavWindow(apply_focus_window); ClosePopupsOverWindow(apply_focus_window, false); FocusWindow(apply_focus_window); if (apply_focus_window->NavLastIds[0] == 0) NavInitWindow(apply_focus_window, false); - // If the window only has a menu layer, select it directly - if (apply_focus_window->DC.NavLayerActiveMask == (1 << ImGuiNavLayer_Menu)) + // If the window has ONLY a menu layer (no main layer), select it directly + // Use NavLayersActiveMaskNext since windows didn't have a chance to be Begin()-ed on this frame, + // so CTRL+Tab where the keys are only held for 1 frame will be able to use correct layers mask since + // the target window as already been previewed once. + // FIXME-NAV: This should be done in NavInit.. or in FocusWindow... However in both of those cases, + // we won't have a guarantee that windows has been visible before and therefore NavLayersActiveMask* + // won't be valid. + if (apply_focus_window->DC.NavLayersActiveMaskNext == (1 << ImGuiNavLayer_Menu)) g.NavLayer = ImGuiNavLayer_Menu; } if (apply_focus_window) @@ -9436,10 +10839,12 @@ static void ImGui::NavUpdateWindowing() // Apply menu/layer toggle if (apply_toggle_layer && g.NavWindow) { + ClearActiveID(); + // Move to parent menu if necessary ImGuiWindow* new_nav_window = g.NavWindow; while (new_nav_window->ParentWindow - && (new_nav_window->DC.NavLayerActiveMask & (1 << ImGuiNavLayer_Menu)) == 0 + && (new_nav_window->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) == 0 && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0) new_nav_window = new_nav_window->ParentWindow; @@ -9449,12 +10854,17 @@ static void ImGui::NavUpdateWindowing() FocusWindow(new_nav_window); new_nav_window->NavLastChildNavWindow = old_nav_window; } - g.NavDisableHighlight = false; - g.NavDisableMouseHover = true; - // When entering a regular menu bar with the Alt key, we always reinitialize the navigation ID. - const ImGuiNavLayer new_nav_layer = (g.NavWindow->DC.NavLayerActiveMask & (1 << ImGuiNavLayer_Menu)) ? (ImGuiNavLayer)((int)g.NavLayer ^ 1) : ImGuiNavLayer_Main; - NavRestoreLayer(new_nav_layer); + // Toggle layer + const ImGuiNavLayer new_nav_layer = (g.NavWindow->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) ? (ImGuiNavLayer)((int)g.NavLayer ^ 1) : ImGuiNavLayer_Main; + if (new_nav_layer != g.NavLayer) + { + // Reinitialize navigation when entering menu bar with the Alt key (FIXME: could be a properly of the layer?) + if (new_nav_layer == ImGuiNavLayer_Menu) + g.NavWindow->NavLastIds[new_nav_layer] = 0; + NavRestoreLayer(new_nav_layer); + NavRestoreHighlightAfterMove(); + } } } @@ -9479,13 +10889,15 @@ void ImGui::NavUpdateWindowingOverlay() if (g.NavWindowingListWindow == NULL) g.NavWindowingListWindow = FindWindowByName("###NavWindowingList"); - SetNextWindowSizeConstraints(ImVec2(g.IO.DisplaySize.x * 0.20f, g.IO.DisplaySize.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX)); - SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f)); + const ImGuiViewport* viewport = GetMainViewport(); + SetNextWindowSizeConstraints(ImVec2(viewport->Size.x * 0.20f, viewport->Size.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX)); + SetNextWindowPos(viewport->GetCenter(), ImGuiCond_Always, ImVec2(0.5f, 0.5f)); PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f); Begin("###NavWindowingList", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings); for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--) { ImGuiWindow* window = g.WindowsFocusOrder[n]; + IM_ASSERT(window != NULL); // Fix static analyzers if (!IsWindowNavFocusable(window)) continue; const char* label = window->Name; @@ -9516,45 +10928,62 @@ void ImGui::ClearDragDrop() memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal)); } -// Call when current ID is active. // When this returns true you need to: a) call SetDragDropPayload() exactly once, b) you may render the payload visual/description, c) call EndDragDropSource() +// If the item has an identifier: +// - This assume/require the item to be activated (typically via ButtonBehavior). +// - Therefore if you want to use this with a mouse button other than left mouse button, it is up to the item itself to activate with another button. +// - We then pull and use the mouse button that was used to activate the item and use it to carry on the drag. +// If the item has no identifier: +// - Currently always assume left mouse button. bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; + // FIXME-DRAGDROP: While in the common-most "drag from non-zero active id" case we can tell the mouse button, + // in both SourceExtern and id==0 cases we may requires something else (explicit flags or some heuristic). + ImGuiMouseButton mouse_button = ImGuiMouseButton_Left; + bool source_drag_active = false; ImGuiID source_id = 0; ImGuiID source_parent_id = 0; - ImGuiMouseButton mouse_button = ImGuiMouseButton_Left; if (!(flags & ImGuiDragDropFlags_SourceExtern)) { - source_id = window->DC.LastItemId; - if (source_id != 0 && g.ActiveId != source_id) // Early out for most common case - return false; - if (g.IO.MouseDown[mouse_button] == false) - return false; - - if (source_id == 0) + source_id = g.LastItemData.ID; + if (source_id != 0) + { + // Common path: items with ID + if (g.ActiveId != source_id) + return false; + if (g.ActiveIdMouseButton != -1) + mouse_button = g.ActiveIdMouseButton; + if (g.IO.MouseDown[mouse_button] == false || window->SkipItems) + return false; + g.ActiveIdAllowOverlap = false; + } + else { + // Uncommon path: items without ID + if (g.IO.MouseDown[mouse_button] == false || window->SkipItems) + return false; + if ((g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) == 0 && (g.ActiveId == 0 || g.ActiveIdWindow != window)) + return false; + // If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to: - // A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag, C) Swallow your programmer pride. + // A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag. if (!(flags & ImGuiDragDropFlags_SourceAllowNullID)) { IM_ASSERT(0); return false; } - // Early out - if ((window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) == 0 && (g.ActiveId == 0 || g.ActiveIdWindow != window)) - return false; - - // Magic fallback (=somehow reprehensible) to handle items with no assigned ID, e.g. Text(), Image() + // Magic fallback to handle items with no assigned ID, e.g. Text(), Image() // We build a throwaway ID based on current ID stack + relative AABB of items in window. - // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING OF THE WIDGET, so if your widget moves your dragging operation will be canceled. + // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING/RESIZINGG OF THE WIDGET, so if your widget moves your dragging operation will be canceled. // We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive. - source_id = window->DC.LastItemId = window->GetIDFromRectangle(window->DC.LastItemRect); - bool is_hovered = ItemHoverable(window->DC.LastItemRect, source_id); + // Rely on keeping other window->LastItemXXX fields intact. + source_id = g.LastItemData.ID = window->GetIDFromRectangle(g.LastItemData.Rect); + bool is_hovered = ItemHoverable(g.LastItemData.Rect, source_id); if (is_hovered && g.IO.MouseClicked[mouse_button]) { SetActiveID(source_id, window); @@ -9563,19 +10992,13 @@ bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags) if (g.ActiveId == source_id) // Allow the underlying widget to display/return hovered during the mouse release frame, else we would get a flicker. g.ActiveIdAllowOverlap = is_hovered; } - else - { - g.ActiveIdAllowOverlap = false; - } if (g.ActiveId != source_id) return false; source_parent_id = window->IDStack.back(); source_drag_active = IsMouseDragging(mouse_button); - // Disable navigation and key inputs while dragging - g.ActiveIdUsingNavDirMask = ~(ImU32)0; - g.ActiveIdUsingNavInputMask = ~(ImU32)0; - g.ActiveIdUsingKeyInputMask = ~(ImU64)0; + // Disable navigation and key inputs while dragging + cancel existing request if any + SetActiveIdUsingNavAndKeys(); } else { @@ -9610,13 +11033,13 @@ bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags) if (g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip)) { ImGuiWindow* tooltip_window = g.CurrentWindow; - tooltip_window->SkipItems = true; + tooltip_window->Hidden = tooltip_window->SkipItems = true; tooltip_window->HiddenFramesCanSkipItems = 1; } } if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern)) - window->DC.LastItemStatusFlags &= ~ImGuiItemStatusFlags_HoveredRect; + g.LastItemData.StatusFlags &= ~ImGuiItemStatusFlags_HoveredRect; return true; } @@ -9679,6 +11102,7 @@ bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_s } payload.DataFrameCount = g.FrameCount; + // Return whether the payload has been accepted return (g.DragDropAcceptFrameCount == g.FrameCount) || (g.DragDropAcceptFrameCount == g.FrameCount - 1); } @@ -9716,14 +11140,14 @@ bool ImGui::BeginDragDropTarget() return false; ImGuiWindow* window = g.CurrentWindow; - if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect)) + if (!(g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect)) return false; ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow; - if (hovered_window == NULL || window->RootWindow != hovered_window->RootWindow) + if (hovered_window == NULL || window->RootWindow != hovered_window->RootWindow || window->SkipItems) return false; - const ImRect& display_rect = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? window->DC.LastItemDisplayRect : window->DC.LastItemRect; - ImGuiID id = window->DC.LastItemId; + const ImRect& display_rect = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? g.LastItemData.DisplayRect : g.LastItemData.Rect; + ImGuiID id = g.LastItemData.ID; if (id == 0) id = window->GetIDFromRectangle(display_rect); if (g.DragDropPayload.SourceId == id) @@ -9765,17 +11189,11 @@ const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDrop } // Render default drop visuals + // FIXME-DRAGDROP: Settle on a proper default visuals for drop target. payload.Preview = was_accepted_previously; flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that lives for 1 frame) if (!(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect) && payload.Preview) - { - // FIXME-DRAG: Settle on a proper default visuals for drop target. - r.Expand(3.5f); - bool push_clip_rect = !window->ClipRect.Contains(r); - if (push_clip_rect) window->DrawList->PushClipRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1)); - window->DrawList->AddRect(r.Min, r.Max, GetColorU32(ImGuiCol_DragDropTarget), 0.0f, ~0, 2.0f); - if (push_clip_rect) window->DrawList->PopClipRect(); - } + window->DrawList->AddRect(r.Min - ImVec2(3.5f,3.5f), r.Max + ImVec2(3.5f, 3.5f), GetColorU32(ImGuiCol_DragDropTarget), 0.0f, 0, 2.0f); g.DragDropAcceptFrameCount = g.FrameCount; payload.Delivery = was_accepted_previously && !IsMouseDown(g.DragDropMouseButton); // For extern drag sources affecting os window focus, it's easier to just test !IsMouseDown() instead of IsMouseReleased() @@ -9805,17 +11223,11 @@ void ImGui::EndDragDropTarget() //----------------------------------------------------------------------------- // All text output from the interface can be captured into tty/file/clipboard. // By default, tree nodes are automatically opened during logging. -//----------------------------------------------------------------------------- - -// Pass text data straight to log (without being displayed) -void ImGui::LogText(const char* fmt, ...) -{ - ImGuiContext& g = *GImGui; - if (!g.LogEnabled) - return; +//----------------------------------------------------------------------------- - va_list args; - va_start(args, fmt); +// Pass text data straight to log (without being displayed) +static inline void LogTextV(ImGuiContext& g, const char* fmt, va_list args) +{ if (g.LogFile) { g.LogBuffer.Buf.resize(0); @@ -9826,59 +11238,88 @@ void ImGui::LogText(const char* fmt, ...) { g.LogBuffer.appendfv(fmt, args); } +} + +void ImGui::LogText(const char* fmt, ...) +{ + ImGuiContext& g = *GImGui; + if (!g.LogEnabled) + return; + + va_list args; + va_start(args, fmt); + LogTextV(g, fmt, args); va_end(args); } +void ImGui::LogTextV(const char* fmt, va_list args) +{ + ImGuiContext& g = *GImGui; + if (!g.LogEnabled) + return; + + LogTextV(g, fmt, args); +} + // Internal version that takes a position to decide on newline placement and pad items according to their depth. // We split text into individual lines to add current tree level padding +// FIXME: This code is a little complicated perhaps, considering simplifying the whole system. void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; + const char* prefix = g.LogNextPrefix; + const char* suffix = g.LogNextSuffix; + g.LogNextPrefix = g.LogNextSuffix = NULL; + if (!text_end) text_end = FindRenderedTextEnd(text, text_end); - const bool log_new_line = ref_pos && (ref_pos->y > g.LogLinePosY + 1); + const bool log_new_line = ref_pos && (ref_pos->y > g.LogLinePosY + g.Style.FramePadding.y + 1); if (ref_pos) g.LogLinePosY = ref_pos->y; if (log_new_line) + { + LogText(IM_NEWLINE); g.LogLineFirstItem = true; + } - const char* text_remaining = text; - if (g.LogDepthRef > window->DC.TreeDepth) // Re-adjust padding if we have popped out of our starting depth + if (prefix) + LogRenderedText(ref_pos, prefix, prefix + strlen(prefix)); // Calculate end ourself to ensure "##" are included here. + + // Re-adjust padding if we have popped out of our starting depth + if (g.LogDepthRef > window->DC.TreeDepth) g.LogDepthRef = window->DC.TreeDepth; const int tree_depth = (window->DC.TreeDepth - g.LogDepthRef); + + const char* text_remaining = text; for (;;) { - // Split the string. Each new line (after a '\n') is followed by spacing corresponding to the current depth of our log entry. - // We don't add a trailing \n to allow a subsequent item on the same line to be captured. + // Split the string. Each new line (after a '\n') is followed by indentation corresponding to the current depth of our log entry. + // We don't add a trailing \n yet to allow a subsequent item on the same line to be captured. const char* line_start = text_remaining; const char* line_end = ImStreolRange(line_start, text_end); - const bool is_first_line = (line_start == text); const bool is_last_line = (line_end == text_end); - if (!is_last_line || (line_start != line_end)) + if (line_start != line_end || !is_last_line) { - const int char_count = (int)(line_end - line_start); - if (log_new_line || !is_first_line) - LogText(IM_NEWLINE "%*s%.*s", tree_depth * 4, "", char_count, line_start); - else if (g.LogLineFirstItem) - LogText("%*s%.*s", tree_depth * 4, "", char_count, line_start); - else - LogText(" %.*s", char_count, line_start); + const int line_length = (int)(line_end - line_start); + const int indentation = g.LogLineFirstItem ? tree_depth * 4 : 1; + LogText("%*s%.*s", indentation, "", line_length, line_start); g.LogLineFirstItem = false; + if (*line_end == '\n') + { + LogText(IM_NEWLINE); + g.LogLineFirstItem = true; + } } - else if (log_new_line) - { - // An empty "" string at a different Y position should output a carriage return. - LogText(IM_NEWLINE); - break; - } - if (is_last_line) break; text_remaining = line_end + 1; } + + if (suffix) + LogRenderedText(ref_pos, suffix, suffix + strlen(suffix)); } // Start logging/capturing text output @@ -9891,12 +11332,21 @@ void ImGui::LogBegin(ImGuiLogType type, int auto_open_depth) IM_ASSERT(g.LogBuffer.empty()); g.LogEnabled = true; g.LogType = type; + g.LogNextPrefix = g.LogNextSuffix = NULL; g.LogDepthRef = window->DC.TreeDepth; g.LogDepthToExpand = ((auto_open_depth >= 0) ? auto_open_depth : g.LogDepthToExpandDefault); g.LogLinePosY = FLT_MAX; g.LogLineFirstItem = true; } +// Important: doesn't copy underlying data, use carefully (prefix/suffix must be in scope at the time of the next LogRenderedText) +void ImGui::LogSetNextTextDecoration(const char* prefix, const char* suffix) +{ + ImGuiContext& g = *GImGui; + g.LogNextPrefix = prefix; + g.LogNextSuffix = suffix; +} + void ImGui::LogToTTY(int auto_open_depth) { ImGuiContext& g = *GImGui; @@ -10305,8 +11755,9 @@ static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandl window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings); } IM_ASSERT(settings->ID == window->ID); - settings->Pos = ImVec2ih((short)window->Pos.x, (short)window->Pos.y); - settings->Size = ImVec2ih((short)window->SizeFull.x, (short)window->SizeFull.y); + settings->Pos = ImVec2ih(window->Pos); + settings->Size = ImVec2ih(window->SizeFull); + settings->Collapsed = window->Collapsed; } @@ -10327,9 +11778,41 @@ static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandl //----------------------------------------------------------------------------- // [SECTION] VIEWPORTS, PLATFORM WINDOWS //----------------------------------------------------------------------------- +// - GetMainViewport() +// - UpdateViewportsNewFrame() [Internal] +// (this section is more complete in the 'docking' branch) +//----------------------------------------------------------------------------- -// (this section is filled in the 'docking' branch) +ImGuiViewport* ImGui::GetMainViewport() +{ + ImGuiContext& g = *GImGui; + return g.Viewports[0]; +} + +// Update viewports and monitor infos +static void ImGui::UpdateViewportsNewFrame() +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(g.Viewports.Size == 1); + + // Update main viewport with current platform position. + // FIXME-VIEWPORT: Size is driven by backend/user code for backward-compatibility but we should aim to make this more consistent. + ImGuiViewportP* main_viewport = g.Viewports[0]; + main_viewport->Flags = ImGuiViewportFlags_IsPlatformWindow | ImGuiViewportFlags_OwnedByApp; + main_viewport->Pos = ImVec2(0.0f, 0.0f); + main_viewport->Size = g.IO.DisplaySize; + + for (int n = 0; n < g.Viewports.Size; n++) + { + ImGuiViewportP* viewport = g.Viewports[n]; + // Lock down space taken by menu bars and status bars, reset the offset for fucntions like BeginMainMenuBar() to alter them again. + viewport->WorkOffsetMin = viewport->BuildWorkOffsetMin; + viewport->WorkOffsetMax = viewport->BuildWorkOffsetMax; + viewport->BuildWorkOffsetMin = viewport->BuildWorkOffsetMax = ImVec2(0.0f, 0.0f); + viewport->UpdateWorkRect(); + } +} //----------------------------------------------------------------------------- // [SECTION] DOCKING @@ -10469,38 +11952,53 @@ static void SetClipboardTextFn_DefaultImpl(void*, const char* text) #endif // Win32 API IME support (for Asian languages, etc.) -#if defined(_WIN32) && !defined(__GNUC__) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS) +#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS) #include #ifdef _MSC_VER #pragma comment(lib, "imm32") #endif -static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y) +static void SetPlatformImeDataFn_DefaultImpl(ImGuiViewport* viewport, ImGuiPlatformImeData* data) { // Notify OS Input Method Editor of text input position - ImGuiIO& io = ImGui::GetIO(); - if (HWND hwnd = (HWND)io.ImeWindowHandle) - if (HIMC himc = ::ImmGetContext(hwnd)) - { - COMPOSITIONFORM cf; - cf.ptCurrentPos.x = x; - cf.ptCurrentPos.y = y; - cf.dwStyle = CFS_FORCE_POSITION; - ::ImmSetCompositionWindow(himc, &cf); - ::ImmReleaseContext(hwnd, himc); - } + HWND hwnd = (HWND)viewport->PlatformHandleRaw; +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + if (hwnd == 0) + hwnd = (HWND)ImGui::GetIO().ImeWindowHandle; +#endif + if (hwnd == 0) + return; + + ::ImmAssociateContextEx(hwnd, NULL, data->WantVisible ? IACE_DEFAULT : 0); + + if (HIMC himc = ::ImmGetContext(hwnd)) + { + COMPOSITIONFORM composition_form = {}; + composition_form.ptCurrentPos.x = (LONG)data->InputPos.x; + composition_form.ptCurrentPos.y = (LONG)data->InputPos.y; + composition_form.dwStyle = CFS_FORCE_POSITION; + ::ImmSetCompositionWindow(himc, &composition_form); + CANDIDATEFORM candidate_form = {}; + candidate_form.dwStyle = CFS_CANDIDATEPOS; + candidate_form.ptCurrentPos.x = (LONG)data->InputPos.x; + candidate_form.ptCurrentPos.y = (LONG)data->InputPos.y; + ::ImmSetCandidateWindow(himc, &candidate_form); + ::ImmReleaseContext(hwnd, himc); + } } #else -static void ImeSetInputScreenPosFn_DefaultImpl(int, int) {} +static void SetPlatformImeDataFn_DefaultImpl(ImGuiViewport*, ImGuiPlatformImeData*) {} #endif //----------------------------------------------------------------------------- // [SECTION] METRICS/DEBUGGER WINDOW //----------------------------------------------------------------------------- +// - RenderViewportThumbnail() [Internal] +// - RenderViewportsThumbnails() [Internal] // - MetricsHelpMarker() [Internal] // - ShowMetricsWindow() // - DebugNodeColumns() [Internal] @@ -10508,13 +12006,66 @@ static void ImeSetInputScreenPosFn_DefaultImpl(int, int) {} // - DebugNodeDrawCmdShowMeshAndBoundingBox() [Internal] // - DebugNodeStorage() [Internal] // - DebugNodeTabBar() [Internal] +// - DebugNodeViewport() [Internal] // - DebugNodeWindow() [Internal] // - DebugNodeWindowSettings() [Internal] // - DebugNodeWindowsList() [Internal] +// - DebugNodeWindowsListByBeginStackParent() [Internal] //----------------------------------------------------------------------------- #ifndef IMGUI_DISABLE_METRICS_WINDOW +void ImGui::DebugRenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP* viewport, const ImRect& bb) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + + ImVec2 scale = bb.GetSize() / viewport->Size; + ImVec2 off = bb.Min - viewport->Pos * scale; + float alpha_mul = 1.0f; + window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border, alpha_mul * 0.40f)); + for (int i = 0; i != g.Windows.Size; i++) + { + ImGuiWindow* thumb_window = g.Windows[i]; + if (!thumb_window->WasActive || (thumb_window->Flags & ImGuiWindowFlags_ChildWindow)) + continue; + + ImRect thumb_r = thumb_window->Rect(); + ImRect title_r = thumb_window->TitleBarRect(); + thumb_r = ImRect(ImFloor(off + thumb_r.Min * scale), ImFloor(off + thumb_r.Max * scale)); + title_r = ImRect(ImFloor(off + title_r.Min * scale), ImFloor(off + ImVec2(title_r.Max.x, title_r.Min.y) * scale) + ImVec2(0,5)); // Exaggerate title bar height + thumb_r.ClipWithFull(bb); + title_r.ClipWithFull(bb); + const bool window_is_focused = (g.NavWindow && thumb_window->RootWindowForTitleBarHighlight == g.NavWindow->RootWindowForTitleBarHighlight); + window->DrawList->AddRectFilled(thumb_r.Min, thumb_r.Max, GetColorU32(ImGuiCol_WindowBg, alpha_mul)); + window->DrawList->AddRectFilled(title_r.Min, title_r.Max, GetColorU32(window_is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg, alpha_mul)); + window->DrawList->AddRect(thumb_r.Min, thumb_r.Max, GetColorU32(ImGuiCol_Border, alpha_mul)); + window->DrawList->AddText(g.Font, g.FontSize * 1.0f, title_r.Min, GetColorU32(ImGuiCol_Text, alpha_mul), thumb_window->Name, FindRenderedTextEnd(thumb_window->Name)); + } + draw_list->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border, alpha_mul)); +} + +static void RenderViewportsThumbnails() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + + // We don't display full monitor bounds (we could, but it often looks awkward), instead we display just enough to cover all of our viewports. + float SCALE = 1.0f / 8.0f; + ImRect bb_full(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX); + for (int n = 0; n < g.Viewports.Size; n++) + bb_full.Add(g.Viewports[n]->GetMainRect()); + ImVec2 p = window->DC.CursorPos; + ImVec2 off = p - bb_full.Min * SCALE; + for (int n = 0; n < g.Viewports.Size; n++) + { + ImGuiViewportP* viewport = g.Viewports[n]; + ImRect viewport_draw_bb(off + (viewport->Pos) * SCALE, off + (viewport->Pos + viewport->Size) * SCALE); + ImGui::DebugRenderViewportThumbnail(window->DrawList, viewport, viewport_draw_bb); + } + ImGui::Dummy(bb_full.GetSize() * SCALE); +} + // Avoid naming collision with imgui_demo.cpp's HelpMarker() for unity builds. static void MetricsHelpMarker(const char* desc) { @@ -10529,24 +12080,29 @@ static void MetricsHelpMarker(const char* desc) } } +#ifndef IMGUI_DISABLE_DEMO_WINDOWS +namespace ImGui { void ShowFontAtlas(ImFontAtlas* atlas); } +#endif + void ImGui::ShowMetricsWindow(bool* p_open) { - if (!Begin("Dear ImGui Metrics/Debugger", p_open)) + ImGuiContext& g = *GImGui; + ImGuiIO& io = g.IO; + ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig; + if (cfg->ShowStackTool) + ShowStackToolWindow(&cfg->ShowStackTool); + + if (!Begin("Dear ImGui Metrics/Debugger", p_open) || GetCurrentWindow()->BeginCount > 1) { End(); return; } - ImGuiContext& g = *GImGui; - ImGuiIO& io = g.IO; - ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig; - // Basic info - Text("Dear ImGui %s", ImGui::GetVersion()); + Text("Dear ImGui %s", GetVersion()); Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3); - Text("%d active windows (%d visible)", io.MetricsActiveWindows, io.MetricsRenderWindows); - Text("%d active allocations", io.MetricsActiveAllocations); + Text("%d visible windows, %d active allocations", io.MetricsRenderWindows, io.MetricsActiveAllocations); //SameLine(); if (SmallButton("GC")) { g.GcCompactAll = true; } Separator(); @@ -10600,11 +12156,10 @@ void ImGui::ShowMetricsWindow(bool* p_open) // Tools if (TreeNode("Tools")) { - // The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted. - if (Button("Item Picker..")) - DebugStartItemPicker(); + // Stack Tool is your best friend! + Checkbox("Show stack tool", &cfg->ShowStackTool); SameLine(); - MetricsHelpMarker("Will call the IM_DEBUG_BREAK() macro to break in debugger.\nWarning: If you don't have a debugger attached, this will probably crash."); + MetricsHelpMarker("You can also call ImGui::ShowStackToolWindow() from your code."); Checkbox("Show windows begin order", &cfg->ShowWindowsBeginOrder); Checkbox("Show windows rectangles", &cfg->ShowWindowsRects); @@ -10622,8 +12177,6 @@ void ImGui::ShowMetricsWindow(bool* p_open) } Unindent(); } - Checkbox("Show ImDrawCmd mesh when hovering", &cfg->ShowDrawCmdMesh); - Checkbox("Show ImDrawCmd bounding boxes when hovering", &cfg->ShowDrawCmdBoundingBoxes); Checkbox("Show tables rectangles", &cfg->ShowTablesRects); SameLine(); @@ -10631,15 +12184,15 @@ void ImGui::ShowMetricsWindow(bool* p_open) cfg->ShowTablesRects |= Combo("##show_table_rects_type", &cfg->ShowTablesRectsType, trt_rects_names, TRT_Count, TRT_Count); if (cfg->ShowTablesRects && g.NavWindow != NULL) { - for (int table_n = 0; table_n < g.Tables.GetSize(); table_n++) + for (int table_n = 0; table_n < g.Tables.GetMapSize(); table_n++) { - ImGuiTable* table = g.Tables.GetByIndex(table_n); - if (table->LastFrameActive < g.FrameCount - 1 || (table->OuterWindow != g.NavWindow && table->InnerWindow != g.NavWindow)) + ImGuiTable* table = g.Tables.TryGetMapData(table_n); + if (table == NULL || table->LastFrameActive < g.FrameCount - 1 || (table->OuterWindow != g.NavWindow && table->InnerWindow != g.NavWindow)) continue; BulletText("Table 0x%08X (%d columns, in '%s')", table->ID, table->ColumnsCount, table->OuterWindow->Name); if (IsItemHovered()) - GetForegroundDrawList()->AddRect(table->OuterRect.Min - ImVec2(1, 1), table->OuterRect.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, ~0, 2.0f); + GetForegroundDrawList()->AddRect(table->OuterRect.Min - ImVec2(1, 1), table->OuterRect.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f); Indent(); char buf[128]; for (int rect_n = 0; rect_n < TRT_Count; rect_n++) @@ -10654,7 +12207,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImFormatString(buf, IM_ARRAYSIZE(buf), "(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) Col %d %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), column_n, trt_rects_names[rect_n]); Selectable(buf); if (IsItemHovered()) - GetForegroundDrawList()->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, ~0, 2.0f); + GetForegroundDrawList()->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f); } } else @@ -10663,23 +12216,71 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImFormatString(buf, IM_ARRAYSIZE(buf), "(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), trt_rects_names[rect_n]); Selectable(buf); if (IsItemHovered()) - GetForegroundDrawList()->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, ~0, 2.0f); + GetForegroundDrawList()->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f); } } Unindent(); } } + // The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted. + if (Button("Item Picker..")) + DebugStartItemPicker(); + SameLine(); + MetricsHelpMarker("Will call the IM_DEBUG_BREAK() macro to break in debugger.\nWarning: If you don't have a debugger attached, this will probably crash."); + + TreePop(); + } + + // Windows + if (TreeNode("Windows", "Windows (%d)", g.Windows.Size)) + { + //SetNextItemOpen(true, ImGuiCond_Once); + DebugNodeWindowsList(&g.Windows, "By display order"); + DebugNodeWindowsList(&g.WindowsFocusOrder, "By focus order (root windows)"); + if (TreeNode("By submission order (begin stack)")) + { + // Here we display windows in their submitted order/hierarchy, however note that the Begin stack doesn't constitute a Parent<>Child relationship! + ImVector& temp_buffer = g.WindowsTempSortBuffer; + temp_buffer.resize(0); + for (int i = 0; i < g.Windows.Size; i++) + if (g.Windows[i]->LastFrameActive + 1 >= g.FrameCount) + temp_buffer.push_back(g.Windows[i]); + struct Func { static int IMGUI_CDECL WindowComparerByBeginOrder(const void* lhs, const void* rhs) { return ((int)(*(const ImGuiWindow* const *)lhs)->BeginOrderWithinContext - (*(const ImGuiWindow* const*)rhs)->BeginOrderWithinContext); } }; + ImQsort(temp_buffer.Data, (size_t)temp_buffer.Size, sizeof(ImGuiWindow*), Func::WindowComparerByBeginOrder); + DebugNodeWindowsListByBeginStackParent(temp_buffer.Data, temp_buffer.Size, NULL); + TreePop(); + } + + TreePop(); + } + + // DrawLists + int drawlist_count = 0; + for (int viewport_i = 0; viewport_i < g.Viewports.Size; viewport_i++) + drawlist_count += g.Viewports[viewport_i]->DrawDataBuilder.GetDrawListCount(); + if (TreeNode("DrawLists", "DrawLists (%d)", drawlist_count)) + { + Checkbox("Show ImDrawCmd mesh when hovering", &cfg->ShowDrawCmdMesh); + Checkbox("Show ImDrawCmd bounding boxes when hovering", &cfg->ShowDrawCmdBoundingBoxes); + for (int viewport_i = 0; viewport_i < g.Viewports.Size; viewport_i++) + { + ImGuiViewportP* viewport = g.Viewports[viewport_i]; + for (int layer_i = 0; layer_i < IM_ARRAYSIZE(viewport->DrawDataBuilder.Layers); layer_i++) + for (int draw_list_i = 0; draw_list_i < viewport->DrawDataBuilder.Layers[layer_i].Size; draw_list_i++) + DebugNodeDrawList(NULL, viewport->DrawDataBuilder.Layers[layer_i][draw_list_i], "DrawList"); + } TreePop(); } - // Contents - DebugNodeWindowsList(&g.Windows, "Windows"); - //DebugNodeWindowList(&g.WindowsFocusOrder, "WindowsFocusOrder"); - if (TreeNode("DrawLists", "Active DrawLists (%d)", g.DrawDataBuilder.Layers[0].Size)) + // Viewports + if (TreeNode("Viewports", "Viewports (%d)", g.Viewports.Size)) { - for (int i = 0; i < g.DrawDataBuilder.Layers[0].Size; i++) - DebugNodeDrawList(NULL, g.DrawDataBuilder.Layers[0][i], "DrawList"); + Indent(GetTreeNodeToLabelSpacing()); + RenderViewportsThumbnails(); + Unindent(GetTreeNodeToLabelSpacing()); + for (int i = 0; i < g.Viewports.Size; i++) + DebugNodeViewport(g.Viewports[i]); TreePop(); } @@ -10695,22 +12296,36 @@ void ImGui::ShowMetricsWindow(bool* p_open) } // Details for TabBars - if (TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.GetSize())) + if (TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.GetAliveCount())) { - for (int n = 0; n < g.TabBars.GetSize(); n++) - DebugNodeTabBar(g.TabBars.GetByIndex(n), "TabBar"); + for (int n = 0; n < g.TabBars.GetMapSize(); n++) + if (ImGuiTabBar* tab_bar = g.TabBars.TryGetMapData(n)) + { + PushID(tab_bar); + DebugNodeTabBar(tab_bar, "TabBar"); + PopID(); + } TreePop(); } // Details for Tables -#ifdef IMGUI_HAS_TABLE - if (TreeNode("Tables", "Tables (%d)", g.Tables.GetSize())) + if (TreeNode("Tables", "Tables (%d)", g.Tables.GetAliveCount())) + { + for (int n = 0; n < g.Tables.GetMapSize(); n++) + if (ImGuiTable* table = g.Tables.TryGetMapData(n)) + DebugNodeTable(table); + TreePop(); + } + + // Details for Fonts +#ifndef IMGUI_DISABLE_DEMO_WINDOWS + ImFontAtlas* atlas = g.IO.Fonts; + if (TreeNode("Fonts", "Fonts (%d)", atlas->Fonts.Size)) { - for (int n = 0; n < g.Tables.GetSize(); n++) - DebugNodeTable(g.Tables.GetByIndex(n)); + ShowFontAtlas(atlas); TreePop(); } -#endif // #ifdef IMGUI_HAS_TABLE +#endif // Details for Docking #ifdef IMGUI_HAS_DOCK @@ -10750,14 +12365,12 @@ void ImGui::ShowMetricsWindow(bool* p_open) TreePop(); } -#ifdef IMGUI_HAS_TABLE if (TreeNode("SettingsTables", "Settings packed data: Tables: %d bytes", g.SettingsTables.size())) { for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings)) DebugNodeTableSettings(settings); TreePop(); } -#endif // #ifdef IMGUI_HAS_TABLE #ifdef IMGUI_HAS_DOCK #endif // #ifdef IMGUI_HAS_DOCK @@ -10773,21 +12386,24 @@ void ImGui::ShowMetricsWindow(bool* p_open) // Misc Details if (TreeNode("Internal state")) { - const char* input_source_names[] = { "None", "Mouse", "Nav", "NavKeyboard", "NavGamepad" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT); - Text("WINDOWING"); Indent(); Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL"); - Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL"); + Text("HoveredWindow->Root: '%s'", g.HoveredWindow ? g.HoveredWindow->RootWindow->Name : "NULL"); Text("HoveredWindowUnderMovingWindow: '%s'", g.HoveredWindowUnderMovingWindow ? g.HoveredWindowUnderMovingWindow->Name : "NULL"); Text("MovingWindow: '%s'", g.MovingWindow ? g.MovingWindow->Name : "NULL"); Unindent(); Text("ITEMS"); Indent(); - Text("ActiveId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d, Source: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer, g.ActiveIdAllowOverlap, input_source_names[g.ActiveIdSource]); + Text("ActiveId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d, Source: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer, g.ActiveIdAllowOverlap, GetInputSourceName(g.ActiveIdSource)); Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL"); - Text("HoveredId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredId, g.HoveredIdPreviousFrame, g.HoveredIdTimer, g.HoveredIdAllowOverlap); // Data is "in-flight" so depending on when the Metrics window is called we may see current frame information or not + + int active_id_using_key_input_count = 0; + for (int n = 0; n < ImGuiKey_NamedKey_COUNT; n++) + active_id_using_key_input_count += g.ActiveIdUsingKeyInputMask[n] ? 1 : 0; + Text("ActiveIdUsing: Wheel: %d, NavDirMask: %X, NavInputMask: %X, KeyInputMask: %d key(s)", g.ActiveIdUsingMouseWheel, g.ActiveIdUsingNavDirMask, g.ActiveIdUsingNavInputMask, active_id_using_key_input_count); + Text("HoveredId: 0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredIdPreviousFrame, g.HoveredIdTimer, g.HoveredIdAllowOverlap); // Not displaying g.HoveredId as it is update mid-frame Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize); Unindent(); @@ -10795,9 +12411,10 @@ void ImGui::ShowMetricsWindow(bool* p_open) Indent(); Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL"); Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer); - Text("NavInputSource: %s", input_source_names[g.NavInputSource]); + Text("NavInputSource: %s", GetInputSourceName(g.NavInputSource)); Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible); - Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId); + Text("NavActivateId/DownId/PressedId/InputId: %08X/%08X/%08X/%08X", g.NavActivateId, g.NavActivateDownId, g.NavActivatePressedId, g.NavActivateInputId); + Text("NavActivateFlags: %04X", g.NavActivateFlags); Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover); Text("NavFocusScopeId = 0x%08X", g.NavFocusScopeId); Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL"); @@ -10831,14 +12448,13 @@ void ImGui::ShowMetricsWindow(bool* p_open) } } -#ifdef IMGUI_HAS_TABLE // Overlay: Display Tables Rectangles if (cfg->ShowTablesRects) { - for (int table_n = 0; table_n < g.Tables.GetSize(); table_n++) + for (int table_n = 0; table_n < g.Tables.GetMapSize(); table_n++) { - ImGuiTable* table = g.Tables.GetByIndex(table_n); - if (table->LastFrameActive < g.FrameCount - 1) + ImGuiTable* table = g.Tables.TryGetMapData(table_n); + if (table == NULL || table->LastFrameActive < g.FrameCount - 1) continue; ImDrawList* draw_list = GetForegroundDrawList(table->OuterWindow); if (cfg->ShowTablesRectsType >= TRT_ColumnsRect) @@ -10848,7 +12464,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImRect r = Funcs::GetTableRect(table, cfg->ShowTablesRectsType, column_n); ImU32 col = (table->HoveredColumnBody == column_n) ? IM_COL32(255, 255, 128, 255) : IM_COL32(255, 0, 128, 255); float thickness = (table->HoveredColumnBody == column_n) ? 3.0f : 1.0f; - draw_list->AddRect(r.Min, r.Max, col, 0.0f, ~0, thickness); + draw_list->AddRect(r.Min, r.Max, col, 0.0f, 0, thickness); } } else @@ -10858,7 +12474,6 @@ void ImGui::ShowMetricsWindow(bool* p_open) } } } -#endif // #ifdef IMGUI_HAS_TABLE #ifdef IMGUI_HAS_DOCK // Overlay: Display Docking info @@ -10870,6 +12485,25 @@ void ImGui::ShowMetricsWindow(bool* p_open) End(); } +// [DEBUG] List fonts in a font atlas and display its texture +void ImGui::ShowFontAtlas(ImFontAtlas* atlas) +{ + for (int i = 0; i < atlas->Fonts.Size; i++) + { + ImFont* font = atlas->Fonts[i]; + PushID(font); + DebugNodeFont(font); + PopID(); + } + if (TreeNode("Atlas texture", "Atlas texture (%dx%d pixels)", atlas->TexWidth, atlas->TexHeight)) + { + ImVec4 tint_col = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); + ImVec4 border_col = ImVec4(1.0f, 1.0f, 1.0f, 0.5f); + Image(atlas->TexID, ImVec2((float)atlas->TexWidth, (float)atlas->TexHeight), ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), tint_col, border_col); + TreePop(); + } +} + // [DEBUG] Display contents of Columns void ImGui::DebugNodeColumns(ImGuiOldColumns* columns) { @@ -10900,7 +12534,7 @@ void ImGui::DebugNodeDrawList(ImGuiWindow* window, const ImDrawList* draw_list, } ImDrawList* fg_draw_list = GetForegroundDrawList(window); // Render additional visuals into the top-most draw list - if (window && IsItemHovered()) + if (window && IsItemHovered() && fg_draw_list) fg_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255)); if (!node_open) return; @@ -10922,7 +12556,7 @@ void ImGui::DebugNodeDrawList(ImGuiWindow* window, const ImDrawList* draw_list, pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w); bool pcmd_node_open = TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "%s", buf); if (IsItemHovered() && (cfg->ShowDrawCmdMesh || cfg->ShowDrawCmdBoundingBoxes) && fg_draw_list) - DebugNodeDrawCmdShowMeshAndBoundingBox(window, draw_list, pcmd, cfg->ShowDrawCmdMesh, cfg->ShowDrawCmdBoundingBoxes); + DebugNodeDrawCmdShowMeshAndBoundingBox(fg_draw_list, draw_list, pcmd, cfg->ShowDrawCmdMesh, cfg->ShowDrawCmdBoundingBoxes); if (!pcmd_node_open) continue; @@ -10943,7 +12577,7 @@ void ImGui::DebugNodeDrawList(ImGuiWindow* window, const ImDrawList* draw_list, ImFormatString(buf, IM_ARRAYSIZE(buf), "Mesh: ElemCount: %d, VtxOffset: +%d, IdxOffset: +%d, Area: ~%0.f px", pcmd->ElemCount, pcmd->VtxOffset, pcmd->IdxOffset, total_area); Selectable(buf); if (IsItemHovered() && fg_draw_list) - DebugNodeDrawCmdShowMeshAndBoundingBox(window, draw_list, pcmd, true, false); + DebugNodeDrawCmdShowMeshAndBoundingBox(fg_draw_list, draw_list, pcmd, true, false); // Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted. ImGuiListClipper clipper; @@ -10966,7 +12600,7 @@ void ImGui::DebugNodeDrawList(ImGuiWindow* window, const ImDrawList* draw_list, { ImDrawListFlags backup_flags = fg_draw_list->Flags; fg_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles. - fg_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), true, 1.0f); + fg_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), ImDrawFlags_Closed, 1.0f); fg_draw_list->Flags = backup_flags; } } @@ -10976,33 +12610,129 @@ void ImGui::DebugNodeDrawList(ImGuiWindow* window, const ImDrawList* draw_list, } // [DEBUG] Display mesh/aabb of a ImDrawCmd -void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImGuiWindow* window, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, bool show_mesh, bool show_aabb) +void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, bool show_mesh, bool show_aabb) { IM_ASSERT(show_mesh || show_aabb); - ImDrawList* fg_draw_list = GetForegroundDrawList(window); // Render additional visuals into the top-most draw list - ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL; - ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data + draw_cmd->VtxOffset; // Draw wire-frame version of all triangles ImRect clip_rect = draw_cmd->ClipRect; ImRect vtxs_rect(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX); - ImDrawListFlags backup_flags = fg_draw_list->Flags; - fg_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles. - for (unsigned int idx_n = draw_cmd->IdxOffset; idx_n < draw_cmd->IdxOffset + draw_cmd->ElemCount; ) + ImDrawListFlags backup_flags = out_draw_list->Flags; + out_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles. + for (unsigned int idx_n = draw_cmd->IdxOffset, idx_end = draw_cmd->IdxOffset + draw_cmd->ElemCount; idx_n < idx_end; ) { + ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL; // We don't hold on those pointers past iterations as ->AddPolyline() may invalidate them if out_draw_list==draw_list + ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data + draw_cmd->VtxOffset; + ImVec2 triangle[3]; for (int n = 0; n < 3; n++, idx_n++) vtxs_rect.Add((triangle[n] = vtx_buffer[idx_buffer ? idx_buffer[idx_n] : idx_n].pos)); if (show_mesh) - fg_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), true, 1.0f); // In yellow: mesh triangles + out_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), ImDrawFlags_Closed, 1.0f); // In yellow: mesh triangles } // Draw bounding boxes if (show_aabb) { - fg_draw_list->AddRect(ImFloor(clip_rect.Min), ImFloor(clip_rect.Max), IM_COL32(255, 0, 255, 255)); // In pink: clipping rectangle submitted to GPU - fg_draw_list->AddRect(ImFloor(vtxs_rect.Min), ImFloor(vtxs_rect.Max), IM_COL32(0, 255, 255, 255)); // In cyan: bounding box of triangles + out_draw_list->AddRect(ImFloor(clip_rect.Min), ImFloor(clip_rect.Max), IM_COL32(255, 0, 255, 255)); // In pink: clipping rectangle submitted to GPU + out_draw_list->AddRect(ImFloor(vtxs_rect.Min), ImFloor(vtxs_rect.Max), IM_COL32(0, 255, 255, 255)); // In cyan: bounding box of triangles + } + out_draw_list->Flags = backup_flags; +} + +// [DEBUG] Display details for a single font, called by ShowStyleEditor(). +void ImGui::DebugNodeFont(ImFont* font) +{ + bool opened = TreeNode(font, "Font: \"%s\"\n%.2f px, %d glyphs, %d file(s)", + font->ConfigData ? font->ConfigData[0].Name : "", font->FontSize, font->Glyphs.Size, font->ConfigDataCount); + SameLine(); + if (SmallButton("Set as default")) + GetIO().FontDefault = font; + if (!opened) + return; + + // Display preview text + PushFont(font); + Text("The quick brown fox jumps over the lazy dog"); + PopFont(); + + // Display details + SetNextItemWidth(GetFontSize() * 8); + DragFloat("Font scale", &font->Scale, 0.005f, 0.3f, 2.0f, "%.1f"); + SameLine(); MetricsHelpMarker( + "Note than the default embedded font is NOT meant to be scaled.\n\n" + "Font are currently rendered into bitmaps at a given size at the time of building the atlas. " + "You may oversample them to get some flexibility with scaling. " + "You can also render at multiple sizes and select which one to use at runtime.\n\n" + "(Glimmer of hope: the atlas system will be rewritten in the future to make scaling more flexible.)"); + Text("Ascent: %f, Descent: %f, Height: %f", font->Ascent, font->Descent, font->Ascent - font->Descent); + char c_str[5]; + Text("Fallback character: '%s' (U+%04X)", ImTextCharToUtf8(c_str, font->FallbackChar), font->FallbackChar); + Text("Ellipsis character: '%s' (U+%04X)", ImTextCharToUtf8(c_str, font->EllipsisChar), font->EllipsisChar); + const int surface_sqrt = (int)ImSqrt((float)font->MetricsTotalSurface); + Text("Texture Area: about %d px ~%dx%d px", font->MetricsTotalSurface, surface_sqrt, surface_sqrt); + for (int config_i = 0; config_i < font->ConfigDataCount; config_i++) + if (font->ConfigData) + if (const ImFontConfig* cfg = &font->ConfigData[config_i]) + BulletText("Input %d: \'%s\', Oversample: (%d,%d), PixelSnapH: %d, Offset: (%.1f,%.1f)", + config_i, cfg->Name, cfg->OversampleH, cfg->OversampleV, cfg->PixelSnapH, cfg->GlyphOffset.x, cfg->GlyphOffset.y); + + // Display all glyphs of the fonts in separate pages of 256 characters + if (TreeNode("Glyphs", "Glyphs (%d)", font->Glyphs.Size)) + { + ImDrawList* draw_list = GetWindowDrawList(); + const ImU32 glyph_col = GetColorU32(ImGuiCol_Text); + const float cell_size = font->FontSize * 1; + const float cell_spacing = GetStyle().ItemSpacing.y; + for (unsigned int base = 0; base <= IM_UNICODE_CODEPOINT_MAX; base += 256) + { + // Skip ahead if a large bunch of glyphs are not present in the font (test in chunks of 4k) + // This is only a small optimization to reduce the number of iterations when IM_UNICODE_MAX_CODEPOINT + // is large // (if ImWchar==ImWchar32 we will do at least about 272 queries here) + if (!(base & 4095) && font->IsGlyphRangeUnused(base, base + 4095)) + { + base += 4096 - 256; + continue; + } + + int count = 0; + for (unsigned int n = 0; n < 256; n++) + if (font->FindGlyphNoFallback((ImWchar)(base + n))) + count++; + if (count <= 0) + continue; + if (!TreeNode((void*)(intptr_t)base, "U+%04X..U+%04X (%d %s)", base, base + 255, count, count > 1 ? "glyphs" : "glyph")) + continue; + + // Draw a 16x16 grid of glyphs + ImVec2 base_pos = GetCursorScreenPos(); + for (unsigned int n = 0; n < 256; n++) + { + // We use ImFont::RenderChar as a shortcut because we don't have UTF-8 conversion functions + // available here and thus cannot easily generate a zero-terminated UTF-8 encoded string. + ImVec2 cell_p1(base_pos.x + (n % 16) * (cell_size + cell_spacing), base_pos.y + (n / 16) * (cell_size + cell_spacing)); + ImVec2 cell_p2(cell_p1.x + cell_size, cell_p1.y + cell_size); + const ImFontGlyph* glyph = font->FindGlyphNoFallback((ImWchar)(base + n)); + draw_list->AddRect(cell_p1, cell_p2, glyph ? IM_COL32(255, 255, 255, 100) : IM_COL32(255, 255, 255, 50)); + if (glyph) + font->RenderChar(draw_list, cell_size, cell_p1, glyph_col, (ImWchar)(base + n)); + if (glyph && IsMouseHoveringRect(cell_p1, cell_p2)) + { + BeginTooltip(); + Text("Codepoint: U+%04X", base + n); + Separator(); + Text("Visible: %d", glyph->Visible); + Text("AdvanceX: %.1f", glyph->AdvanceX); + Text("Pos: (%.2f,%.2f)->(%.2f,%.2f)", glyph->X0, glyph->Y0, glyph->X1, glyph->Y1); + Text("UV: (%.3f,%.3f)->(%.3f,%.3f)", glyph->U0, glyph->V0, glyph->U1, glyph->V1); + EndTooltip(); + } + } + Dummy(ImVec2((cell_size + cell_spacing) * 16, (cell_size + cell_spacing) * 16)); + TreePop(); + } + TreePop(); } - fg_draw_list->Flags = backup_flags; + TreePop(); } // [DEBUG] Display contents of ImGuiStorage @@ -11027,9 +12757,16 @@ void ImGui::DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label) const char* buf_end = buf + IM_ARRAYSIZE(buf); const bool is_active = (tab_bar->PrevFrameVisible >= GetFrameCount() - 2); p += ImFormatString(p, buf_end - p, "%s 0x%08X (%d tabs)%s", label, tab_bar->ID, tab_bar->Tabs.Size, is_active ? "" : " *Inactive*"); - IM_UNUSED(p); + p += ImFormatString(p, buf_end - p, " { "); + for (int tab_n = 0; tab_n < ImMin(tab_bar->Tabs.Size, 3); tab_n++) + { + ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; + p += ImFormatString(p, buf_end - p, "%s'%s'", + tab_n > 0 ? ", " : "", (tab->NameOffset != -1) ? tab_bar->GetTabName(tab) : "???"); + } + p += ImFormatString(p, buf_end - p, (tab_bar->Tabs.Size > 3) ? " ... }" : " } "); if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); } - bool open = TreeNode(tab_bar, "%s", buf); + bool open = TreeNode(label, "%s", buf); if (!is_active) { PopStyleColor(); } if (is_active && IsItemHovered()) { @@ -11047,13 +12784,33 @@ void ImGui::DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label) if (SmallButton("<")) { TabBarQueueReorder(tab_bar, tab, -1); } SameLine(0, 2); if (SmallButton(">")) { TabBarQueueReorder(tab_bar, tab, +1); } SameLine(); Text("%02d%c Tab 0x%08X '%s' Offset: %.1f, Width: %.1f/%.1f", - tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID, (tab->NameOffset != -1) ? tab_bar->GetTabName(tab) : "", tab->Offset, tab->Width, tab->ContentWidth); + tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID, (tab->NameOffset != -1) ? tab_bar->GetTabName(tab) : "???", tab->Offset, tab->Width, tab->ContentWidth); PopID(); } TreePop(); } } +void ImGui::DebugNodeViewport(ImGuiViewportP* viewport) +{ + SetNextItemOpen(true, ImGuiCond_Once); + if (TreeNode("viewport0", "Viewport #%d", 0)) + { + ImGuiWindowFlags flags = viewport->Flags; + BulletText("Main Pos: (%.0f,%.0f), Size: (%.0f,%.0f)\nWorkArea Offset Left: %.0f Top: %.0f, Right: %.0f, Bottom: %.0f", + viewport->Pos.x, viewport->Pos.y, viewport->Size.x, viewport->Size.y, + viewport->WorkOffsetMin.x, viewport->WorkOffsetMin.y, viewport->WorkOffsetMax.x, viewport->WorkOffsetMax.y); + BulletText("Flags: 0x%04X =%s%s%s", viewport->Flags, + (flags & ImGuiViewportFlags_IsPlatformWindow) ? " IsPlatformWindow" : "", + (flags & ImGuiViewportFlags_IsPlatformMonitor) ? " IsPlatformMonitor" : "", + (flags & ImGuiViewportFlags_OwnedByApp) ? " OwnedByApp" : ""); + for (int layer_i = 0; layer_i < IM_ARRAYSIZE(viewport->DrawDataBuilder.Layers); layer_i++) + for (int draw_list_i = 0; draw_list_i < viewport->DrawDataBuilder.Layers[layer_i].Size; draw_list_i++) + DebugNodeDrawList(NULL, viewport->DrawDataBuilder.Layers[layer_i][draw_list_i], "DrawList"); + TreePop(); + } +} + void ImGui::DebugNodeWindow(ImGuiWindow* window, const char* label) { if (window == NULL) @@ -11086,12 +12843,19 @@ void ImGui::DebugNodeWindow(ImGuiWindow* window, const char* label) BulletText("Scroll: (%.2f/%.2f,%.2f/%.2f) Scrollbar:%s%s", window->Scroll.x, window->ScrollMax.x, window->Scroll.y, window->ScrollMax.y, window->ScrollbarX ? "X" : "", window->ScrollbarY ? "Y" : ""); BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1); BulletText("Appearing: %d, Hidden: %d (CanSkip %d Cannot %d), SkipItems: %d", window->Appearing, window->Hidden, window->HiddenFramesCanSkipItems, window->HiddenFramesCannotSkipItems, window->SkipItems); - BulletText("NavLastIds: 0x%08X,0x%08X, NavLayerActiveMask: %X", window->NavLastIds[0], window->NavLastIds[1], window->DC.NavLayerActiveMask); - BulletText("NavLastChildNavWindow: %s", window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL"); - if (!window->NavRectRel[0].IsInverted()) - BulletText("NavRectRel[0]: (%.1f,%.1f)(%.1f,%.1f)", window->NavRectRel[0].Min.x, window->NavRectRel[0].Min.y, window->NavRectRel[0].Max.x, window->NavRectRel[0].Max.y); - else - BulletText("NavRectRel[0]: "); + for (int layer = 0; layer < ImGuiNavLayer_COUNT; layer++) + { + ImRect r = window->NavRectRel[layer]; + if (r.Min.x >= r.Max.y && r.Min.y >= r.Max.y) + { + BulletText("NavLastIds[%d]: 0x%08X", layer, window->NavLastIds[layer]); + continue; + } + BulletText("NavLastIds[%d]: 0x%08X at +(%.1f,%.1f)(%.1f,%.1f)", layer, window->NavLastIds[layer], r.Min.x, r.Min.y, r.Max.x, r.Max.y); + if (IsItemHovered()) + GetForegroundDrawList(window)->AddRect(r.Min + window->Pos, r.Max + window->Pos, IM_COL32(255, 255, 0, 255)); + } + BulletText("NavLayersActiveMask: %X, NavLastChildNavWindow: %s", window->DC.NavLayersActiveMask, window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL"); if (window->RootWindow != window) { DebugNodeWindow(window->RootWindow, "RootWindow"); } if (window->ParentWindow != NULL) { DebugNodeWindow(window->ParentWindow, "ParentWindow"); } if (window->DC.ChildWindows.Size > 0) { DebugNodeWindowsList(&window->DC.ChildWindows, "ChildWindows"); } @@ -11111,12 +12875,10 @@ void ImGui::DebugNodeWindowSettings(ImGuiWindowSettings* settings) settings->ID, settings->GetName(), settings->Pos.x, settings->Pos.y, settings->Size.x, settings->Size.y, settings->Collapsed); } - void ImGui::DebugNodeWindowsList(ImVector* windows, const char* label) { if (!TreeNode(label, "%s (%d)", label, windows->Size)) return; - Text("(In front-to-back order:)"); for (int i = windows->Size - 1; i >= 0; i--) // Iterate front to back { PushID((*windows)[i]); @@ -11126,19 +12888,227 @@ void ImGui::DebugNodeWindowsList(ImVector* windows, const char* la TreePop(); } +// FIXME-OPT: This is technically suboptimal, but it is simpler this way. +void ImGui::DebugNodeWindowsListByBeginStackParent(ImGuiWindow** windows, int windows_size, ImGuiWindow* parent_in_begin_stack) +{ + for (int i = 0; i < windows_size; i++) + { + ImGuiWindow* window = windows[i]; + if (window->ParentWindowInBeginStack != parent_in_begin_stack) + continue; + char buf[20]; + ImFormatString(buf, IM_ARRAYSIZE(buf), "[%04d] Window", window->BeginOrderWithinContext); + //BulletText("[%04d] Window '%s'", window->BeginOrderWithinContext, window->Name); + DebugNodeWindow(window, buf); + Indent(); + DebugNodeWindowsListByBeginStackParent(windows + i + 1, windows_size - i - 1, window); + Unindent(); + } +} + +//----------------------------------------------------------------------------- +// [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, STACK TOOL) +//----------------------------------------------------------------------------- + +// [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack. +void ImGui::UpdateDebugToolItemPicker() +{ + ImGuiContext& g = *GImGui; + g.DebugItemPickerBreakId = 0; + if (!g.DebugItemPickerActive) + return; + + const ImGuiID hovered_id = g.HoveredIdPreviousFrame; + SetMouseCursor(ImGuiMouseCursor_Hand); + if (IsKeyPressed(ImGuiKey_Escape)) + g.DebugItemPickerActive = false; + if (IsMouseClicked(0) && hovered_id) + { + g.DebugItemPickerBreakId = hovered_id; + g.DebugItemPickerActive = false; + } + SetNextWindowBgAlpha(0.60f); + BeginTooltip(); + Text("HoveredId: 0x%08X", hovered_id); + Text("Press ESC to abort picking."); + TextColored(GetStyleColorVec4(hovered_id ? ImGuiCol_Text : ImGuiCol_TextDisabled), "Click to break in debugger!"); + EndTooltip(); +} + +// [DEBUG] Stack Tool: update queries. Called by NewFrame() +void ImGui::UpdateDebugToolStackQueries() +{ + ImGuiContext& g = *GImGui; + ImGuiStackTool* tool = &g.DebugStackTool; + + // Clear hook when stack tool is not visible + g.DebugHookIdInfo = 0; + if (g.FrameCount != tool->LastActiveFrame + 1) + return; + + // Update queries. The steps are: -1: query Stack, >= 0: query each stack item + // We can only perform 1 ID Info query every frame. This is designed so the GetID() tests are cheap and constant-time + const ImGuiID query_id = g.HoveredIdPreviousFrame ? g.HoveredIdPreviousFrame : g.ActiveId; + if (tool->QueryId != query_id) + { + tool->QueryId = query_id; + tool->StackLevel = -1; + tool->Results.resize(0); + } + if (query_id == 0) + return; + + // Advance to next stack level when we got our result, or after 2 frames (in case we never get a result) + int stack_level = tool->StackLevel; + if (stack_level >= 0 && stack_level < tool->Results.Size) + if (tool->Results[stack_level].QuerySuccess || tool->Results[stack_level].QueryFrameCount > 2) + tool->StackLevel++; + + // Update hook + stack_level = tool->StackLevel; + if (stack_level == -1) + g.DebugHookIdInfo = query_id; + if (stack_level >= 0 && stack_level < tool->Results.Size) + { + g.DebugHookIdInfo = tool->Results[stack_level].ID; + tool->Results[stack_level].QueryFrameCount++; + } +} + +// [DEBUG] Stack tool: hooks called by GetID() family functions +void ImGui::DebugHookIdInfo(ImGuiID id, ImGuiDataType data_type, const void* data_id, const void* data_id_end) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + ImGuiStackTool* tool = &g.DebugStackTool; + + // Step 0: stack query + // This assume that the ID was computed with the current ID stack, which tends to be the case for our widget. + if (tool->StackLevel == -1) + { + tool->StackLevel++; + tool->Results.resize(window->IDStack.Size + 1, ImGuiStackLevelInfo()); + for (int n = 0; n < window->IDStack.Size + 1; n++) + tool->Results[n].ID = (n < window->IDStack.Size) ? window->IDStack[n] : id; + return; + } + + // Step 1+: query for individual level + IM_ASSERT(tool->StackLevel >= 0); + if (tool->StackLevel != window->IDStack.Size) + return; + ImGuiStackLevelInfo* info = &tool->Results[tool->StackLevel]; + IM_ASSERT(info->ID == id && info->QueryFrameCount > 0); + + int data_len; + switch (data_type) + { + case ImGuiDataType_S32: + ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "%d", (int)(intptr_t)data_id); + break; + case ImGuiDataType_String: + data_len = data_id_end ? (int)((const char*)data_id_end - (const char*)data_id) : (int)strlen((const char*)data_id); + ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "\"%.*s\"", data_len, (const char*)data_id); + break; + case ImGuiDataType_Pointer: + ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "(void*)0x%p", data_id); + break; + case ImGuiDataType_ID: + if (info->Desc[0] == 0) // PushOverrideID() is often used to avoid hashing twice, which would lead to 2 calls to DebugHookIdInfo(). We prioritize the first one. + ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "0x%08X [override]", id); + break; + default: + IM_ASSERT(0); + } + info->QuerySuccess = true; +} + +// Stack Tool: Display UI +void ImGui::ShowStackToolWindow(bool* p_open) +{ + ImGuiContext& g = *GImGui; + if (!(g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize)) + SetNextWindowSize(ImVec2(0.0f, GetFontSize() * 8.0f), ImGuiCond_FirstUseEver); + if (!Begin("Dear ImGui Stack Tool", p_open) || GetCurrentWindow()->BeginCount > 1) + { + End(); + return; + } + + // Display hovered/active status + const ImGuiID hovered_id = g.HoveredIdPreviousFrame; + const ImGuiID active_id = g.ActiveId; +#ifdef IMGUI_ENABLE_TEST_ENGINE + Text("HoveredId: 0x%08X (\"%s\"), ActiveId: 0x%08X (\"%s\")", hovered_id, hovered_id ? ImGuiTestEngine_FindItemDebugLabel(&g, hovered_id) : "", active_id, active_id ? ImGuiTestEngine_FindItemDebugLabel(&g, active_id) : ""); +#else + Text("HoveredId: 0x%08X, ActiveId: 0x%08X", hovered_id, active_id); +#endif + SameLine(); + MetricsHelpMarker("Hover an item with the mouse to display elements of the ID Stack leading to the item's final ID.\nEach level of the stack correspond to a PushID() call.\nAll levels of the stack are hashed together to make the final ID of a widget (ID displayed at the bottom level of the stack).\nRead FAQ entry about the ID stack for details."); + + // Display decorated stack + ImGuiStackTool* tool = &g.DebugStackTool; + tool->LastActiveFrame = g.FrameCount; + if (tool->Results.Size > 0 && BeginTable("##table", 3, ImGuiTableFlags_Borders)) + { + const float id_width = CalcTextSize("0xDDDDDDDD").x; + TableSetupColumn("Seed", ImGuiTableColumnFlags_WidthFixed, id_width); + TableSetupColumn("PushID", ImGuiTableColumnFlags_WidthStretch); + TableSetupColumn("Result", ImGuiTableColumnFlags_WidthFixed, id_width); + TableHeadersRow(); + for (int n = 0; n < tool->Results.Size; n++) + { + ImGuiStackLevelInfo* info = &tool->Results[n]; + TableNextColumn(); + Text("0x%08X", (n > 0) ? tool->Results[n - 1].ID : 0); + + TableNextColumn(); + ImGuiWindow* window = (info->Desc[0] == 0 && n == 0) ? FindWindowByID(info->ID) : NULL; + if (window) // Source: window name (because the root ID don't call GetID() and so doesn't get hooked) + Text("\"%s\" [window]", window->Name); + else if (info->QuerySuccess) // Source: GetID() hooks (prioritize over ItemInfo() because we frequently use patterns like: PushID(str), Button("") where they both have same id) + TextUnformatted(info->Desc); + else if (tool->StackLevel >= tool->Results.Size) // Only start using fallback below when all queries are done, so during queries we don't flickering ??? markers. + { +#ifdef IMGUI_ENABLE_TEST_ENGINE + if (const char* label = ImGuiTestEngine_FindItemDebugLabel(&g, info->ID)) // Source: ImGuiTestEngine's ItemInfo() + Text("??? \"%s\"", label); + else +#endif + TextUnformatted("???"); + } + + TableNextColumn(); + Text("0x%08X", info->ID); + if (n == tool->Results.Size - 1) + TableSetBgColor(ImGuiTableBgTarget_CellBg, GetColorU32(ImGuiCol_Header)); + } + EndTable(); + } + End(); +} + #else void ImGui::ShowMetricsWindow(bool*) {} +void ImGui::ShowFontAtlas(ImFontAtlas*) {} void ImGui::DebugNodeColumns(ImGuiOldColumns*) {} void ImGui::DebugNodeDrawList(ImGuiWindow*, const ImDrawList*, const char*) {} -void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImGuiWindow*, const ImDrawList*, const ImDrawCmd*, bool, bool) {} +void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList*, const ImDrawList*, const ImDrawCmd*, bool, bool) {} +void ImGui::DebugNodeFont(ImFont*) {} void ImGui::DebugNodeStorage(ImGuiStorage*, const char*) {} void ImGui::DebugNodeTabBar(ImGuiTabBar*, const char*) {} void ImGui::DebugNodeWindow(ImGuiWindow*, const char*) {} void ImGui::DebugNodeWindowSettings(ImGuiWindowSettings*) {} void ImGui::DebugNodeWindowsList(ImVector*, const char*) {} +void ImGui::DebugNodeViewport(ImGuiViewportP*) {} -#endif +void ImGui::ShowStackToolWindow(bool*) {} +void ImGui::DebugHookIdInfo(ImGuiID, ImGuiDataType, const void*, const void*) {} +void ImGui::UpdateDebugToolItemPicker() {} +void ImGui::UpdateDebugToolStackQueries() {} + +#endif // #ifndef IMGUI_DISABLE_METRICS_WINDOW //----------------------------------------------------------------------------- diff --git a/apps/common/external/imgui/imgui.h b/apps/common/external/imgui/imgui.h index 99f03dec31..871b75f00b 100644 --- a/apps/common/external/imgui/imgui.h +++ b/apps/common/external/imgui/imgui.h @@ -1,4 +1,4 @@ -// dear imgui, v1.80 +// dear imgui, v1.87 // (headers) // Help: @@ -11,11 +11,15 @@ // - FAQ http://dearimgui.org/faq // - Homepage & latest https://github.com/ocornut/imgui // - Releases & changelog https://github.com/ocornut/imgui/releases -// - Gallery https://github.com/ocornut/imgui/issues/3488 (please post your screenshots/video there!) +// - Gallery https://github.com/ocornut/imgui/issues/4451 (please post your screenshots/video there!) +// - Wiki https://github.com/ocornut/imgui/wiki (lots of good stuff there) // - Glossary https://github.com/ocornut/imgui/wiki/Glossary -// - Wiki https://github.com/ocornut/imgui/wiki // - Issues & support https://github.com/ocornut/imgui/issues +// Getting Started? +// - For first-time users having issues compiling/linking/running or issues loading fonts: +// please post in https://github.com/ocornut/imgui/discussions if you cannot find a solution in resources above. + /* Index of this file: @@ -27,10 +31,12 @@ Index of this file: // [SECTION] ImGuiStyle // [SECTION] ImGuiIO // [SECTION] Misc data structures (ImGuiInputTextCallbackData, ImGuiSizeCallbackData, ImGuiPayload, ImGuiTableSortSpecs, ImGuiTableColumnSortSpecs) -// [SECTION] Obsolete functions // [SECTION] Helpers (ImGuiOnceUponAFrame, ImGuiTextFilter, ImGuiTextBuffer, ImGuiStorage, ImGuiListClipper, ImColor) -// [SECTION] Drawing API (ImDrawCallback, ImDrawCmd, ImDrawIdx, ImDrawVert, ImDrawChannel, ImDrawListSplitter, ImDrawListFlags, ImDrawList, ImDrawData) +// [SECTION] Drawing API (ImDrawCallback, ImDrawCmd, ImDrawIdx, ImDrawVert, ImDrawChannel, ImDrawListSplitter, ImDrawFlags, ImDrawListFlags, ImDrawList, ImDrawData) // [SECTION] Font API (ImFontConfig, ImFontGlyph, ImFontGlyphRangesBuilder, ImFontAtlasFlags, ImFontAtlas, ImFont) +// [SECTION] Viewports (ImGuiViewportFlags, ImGuiViewport) +// [SECTION] Platform Dependent Interfaces (ImGuiPlatformImeData) +// [SECTION] Obsolete functions and types */ @@ -58,8 +64,8 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) -#define IMGUI_VERSION "1.80" -#define IMGUI_VERSION_NUM 18000 +#define IMGUI_VERSION "1.87" +#define IMGUI_VERSION_NUM 18700 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE @@ -80,25 +86,34 @@ Index of this file: #endif #define IM_ARRAYSIZE(_ARR) ((int)(sizeof(_ARR) / sizeof(*(_ARR)))) // Size of a static C-style array. Don't use on pointers! #define IM_UNUSED(_VAR) ((void)(_VAR)) // Used to silence "unused variable warnings". Often useful as asserts may be stripped out from final builds. -#if (__cplusplus >= 201100) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201100) #define IM_OFFSETOF(_TYPE,_MEMBER) offsetof(_TYPE, _MEMBER) // Offset of _MEMBER within _TYPE. Standardized as offsetof() in C++11 -#else -#define IM_OFFSETOF(_TYPE,_MEMBER) ((size_t)&(((_TYPE*)0)->_MEMBER)) // Offset of _MEMBER within _TYPE. Old style macro. -#endif // Helper Macros - IM_FMTARGS, IM_FMTLIST: Apply printf-style warnings to our formatting functions. -#if !defined(IMGUI_USE_STB_SPRINTF) && defined(__clang__) -#define IM_FMTARGS(FMT) __attribute__((format(printf, FMT, FMT+1))) -#define IM_FMTLIST(FMT) __attribute__((format(printf, FMT, 0))) -#elif !defined(IMGUI_USE_STB_SPRINTF) && defined(__GNUC__) && defined(__MINGW32__) +#if !defined(IMGUI_USE_STB_SPRINTF) && defined(__MINGW32__) && !defined(__clang__) #define IM_FMTARGS(FMT) __attribute__((format(gnu_printf, FMT, FMT+1))) #define IM_FMTLIST(FMT) __attribute__((format(gnu_printf, FMT, 0))) +#elif !defined(IMGUI_USE_STB_SPRINTF) && (defined(__clang__) || defined(__GNUC__)) +#define IM_FMTARGS(FMT) __attribute__((format(printf, FMT, FMT+1))) +#define IM_FMTLIST(FMT) __attribute__((format(printf, FMT, 0))) #else #define IM_FMTARGS(FMT) #define IM_FMTLIST(FMT) #endif +// Disable some of MSVC most aggressive Debug runtime checks in function header/footer (used in some simple/low-level functions) +#if defined(_MSC_VER) && !defined(__clang__) && !defined(__INTEL_COMPILER) && !defined(IMGUI_DEBUG_PARANOID) +#define IM_MSVC_RUNTIME_CHECKS_OFF __pragma(runtime_checks("",off)) __pragma(check_stack(off)) __pragma(strict_gs_check(push,off)) +#define IM_MSVC_RUNTIME_CHECKS_RESTORE __pragma(runtime_checks("",restore)) __pragma(check_stack()) __pragma(strict_gs_check(pop)) +#else +#define IM_MSVC_RUNTIME_CHECKS_OFF +#define IM_MSVC_RUNTIME_CHECKS_RESTORE +#endif + // Warnings +#ifdef _MSC_VER +#pragma warning (push) +#pragma warning (disable: 26495) // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6). +#endif #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wold-style-cast" @@ -125,6 +140,7 @@ struct ImDrawListSplitter; // Helper to split a draw list into differen struct ImDrawVert; // A single vertex (pos + uv + col = 20 bytes by default. Override layout with IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT) struct ImFont; // Runtime data for a single font within a parent ImFontAtlas struct ImFontAtlas; // Runtime data for multiple fonts, bake multiple fonts into a single texture, TTF/OTF font loader +struct ImFontBuilderIO; // Opaque interface to a font builder (stb_truetype or FreeType). struct ImFontConfig; // Configuration data when adding a font or merging fonts struct ImFontGlyph; // A single font glyph (code point + coordinates within in ImFontAtlas + offset) struct ImFontGlyphRangesBuilder; // Helper to build glyph ranges from text/string data @@ -132,9 +148,11 @@ struct ImColor; // Helper functions to create a color that c struct ImGuiContext; // Dear ImGui context (opaque structure, unless including imgui_internal.h) struct ImGuiIO; // Main configuration and I/O between your application and ImGui struct ImGuiInputTextCallbackData; // Shared state of InputText() when using custom ImGuiInputTextCallback (rare/advanced use) +struct ImGuiKeyData; // Storage for ImGuiIO and IsKeyDown(), IsKeyPressed() etc functions. struct ImGuiListClipper; // Helper to manually clip large list of items -struct ImGuiOnceUponAFrame; // Helper for running a block of code not more than once a frame, used by IMGUI_ONCE_UPON_A_FRAME macro +struct ImGuiOnceUponAFrame; // Helper for running a block of code not more than once a frame struct ImGuiPayload; // User data payload for drag and drop operations +struct ImGuiPlatformImeData; // Platform IME data for io.SetPlatformImeDataFn() function. struct ImGuiSizeCallbackData; // Callback data when using SetNextWindowSizeConstraints() (rare/advanced use) struct ImGuiStorage; // Helper for key->value storage struct ImGuiStyle; // Runtime data for styling/colors @@ -142,8 +160,9 @@ struct ImGuiTableSortSpecs; // Sorting specifications for a table (often struct ImGuiTableColumnSortSpecs; // Sorting specification for one column of a table struct ImGuiTextBuffer; // Helper to hold and append into a text buffer (~string builder) struct ImGuiTextFilter; // Helper to parse and apply text filters (e.g. "aaaaa[,bbbbb][,ccccc]") +struct ImGuiViewport; // A Platform Window (always only one in 'master' branch), in the future may represent Platform Monitor -// Enums/Flags (declared as int for compatibility with old C++, to allow using as flags and to not pollute the top of this file) +// Enums/Flags (declared as int for compatibility with old C++, to allow using as flags without overhead, and to not pollute the top of this file) // - Tip: Use your programming IDE navigation facilities on the names in the _central column_ below to find the actual flags/enum lists! // In Visual Studio IDE: CTRL+comma ("Edit.NavigateTo") can follow symbols in comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot. // With Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols in comments. @@ -151,15 +170,15 @@ typedef int ImGuiCol; // -> enum ImGuiCol_ // Enum: A typedef int ImGuiCond; // -> enum ImGuiCond_ // Enum: A condition for many Set*() functions typedef int ImGuiDataType; // -> enum ImGuiDataType_ // Enum: A primary data type typedef int ImGuiDir; // -> enum ImGuiDir_ // Enum: A cardinal direction -typedef int ImGuiKey; // -> enum ImGuiKey_ // Enum: A key identifier (ImGui-side enum) +typedef int ImGuiKey; // -> enum ImGuiKey_ // Enum: A key identifier typedef int ImGuiNavInput; // -> enum ImGuiNavInput_ // Enum: An input identifier for navigation typedef int ImGuiMouseButton; // -> enum ImGuiMouseButton_ // Enum: A mouse button identifier (0=left, 1=right, 2=middle) typedef int ImGuiMouseCursor; // -> enum ImGuiMouseCursor_ // Enum: A mouse cursor identifier typedef int ImGuiSortDirection; // -> enum ImGuiSortDirection_ // Enum: A sorting direction (ascending or descending) typedef int ImGuiStyleVar; // -> enum ImGuiStyleVar_ // Enum: A variable identifier for styling typedef int ImGuiTableBgTarget; // -> enum ImGuiTableBgTarget_ // Enum: A color target for TableSetBgColor() -typedef int ImDrawCornerFlags; // -> enum ImDrawCornerFlags_ // Flags: for ImDrawList::AddRect(), AddRectFilled() etc. -typedef int ImDrawListFlags; // -> enum ImDrawListFlags_ // Flags: for ImDrawList +typedef int ImDrawFlags; // -> enum ImDrawFlags_ // Flags: for ImDrawList functions +typedef int ImDrawListFlags; // -> enum ImDrawListFlags_ // Flags: for ImDrawList instance typedef int ImFontAtlasFlags; // -> enum ImFontAtlasFlags_ // Flags: for ImFontAtlas build typedef int ImGuiBackendFlags; // -> enum ImGuiBackendFlags_ // Flags: for io.BackendFlags typedef int ImGuiButtonFlags; // -> enum ImGuiButtonFlags_ // Flags: for InvisibleButton() @@ -180,46 +199,53 @@ typedef int ImGuiTableFlags; // -> enum ImGuiTableFlags_ // Flags: F typedef int ImGuiTableColumnFlags; // -> enum ImGuiTableColumnFlags_// Flags: For TableSetupColumn() typedef int ImGuiTableRowFlags; // -> enum ImGuiTableRowFlags_ // Flags: For TableNextRow() typedef int ImGuiTreeNodeFlags; // -> enum ImGuiTreeNodeFlags_ // Flags: for TreeNode(), TreeNodeEx(), CollapsingHeader() +typedef int ImGuiViewportFlags; // -> enum ImGuiViewportFlags_ // Flags: for ImGuiViewport typedef int ImGuiWindowFlags; // -> enum ImGuiWindowFlags_ // Flags: for Begin(), BeginChild() -// Other types -#ifndef ImTextureID // ImTextureID [configurable type: override in imconfig.h with '#define ImTextureID xxx'] -typedef void* ImTextureID; // User data for rendering backend to identify a texture. This is whatever to you want it to be! read the FAQ about ImTextureID for details. +// ImTexture: user data for renderer backend to identify a texture [Compile-time configurable type] +// - To use something else than an opaque void* pointer: override with e.g. '#define ImTextureID MyTextureType*' in your imconfig.h file. +// - This can be whatever to you want it to be! read the FAQ about ImTextureID for details. +#ifndef ImTextureID +typedef void* ImTextureID; // Default: store a pointer or an integer fitting in a pointer (most renderer backends are ok with that) #endif -typedef unsigned int ImGuiID; // A unique ID used by widgets, typically hashed from a stack of string. -typedef int (*ImGuiInputTextCallback)(ImGuiInputTextCallbackData* data); // Callback function for ImGui::InputText() -typedef void (*ImGuiSizeCallback)(ImGuiSizeCallbackData* data); // Callback function for ImGui::SetNextWindowSizeConstraints() -// Character types -// (we generally use UTF-8 encoded string in the API. This is storage specifically for a decoded character used for keyboard input and display) -typedef unsigned short ImWchar16; // A single decoded U16 character/code point. We encode them as multi bytes UTF-8 when used in strings. -typedef unsigned int ImWchar32; // A single decoded U32 character/code point. We encode them as multi bytes UTF-8 when used in strings. -#ifdef IMGUI_USE_WCHAR32 // ImWchar [configurable type: override in imconfig.h with '#define IMGUI_USE_WCHAR32' to support Unicode planes 1-16] -typedef ImWchar32 ImWchar; -#else -typedef ImWchar16 ImWchar; +// ImDrawIdx: vertex index. [Compile-time configurable type] +// - To use 16-bit indices + allow large meshes: backend need to set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset' and handle ImDrawCmd::VtxOffset (recommended). +// - To use 32-bit indices: override with '#define ImDrawIdx unsigned int' in your imconfig.h file. +#ifndef ImDrawIdx +typedef unsigned short ImDrawIdx; // Default: 16-bit (for maximum compatibility with renderer backends) #endif -// Basic scalar data types +// Scalar data types +typedef unsigned int ImGuiID;// A unique ID used by widgets (typically the result of hashing a stack of string) typedef signed char ImS8; // 8-bit signed integer typedef unsigned char ImU8; // 8-bit unsigned integer typedef signed short ImS16; // 16-bit signed integer typedef unsigned short ImU16; // 16-bit unsigned integer typedef signed int ImS32; // 32-bit signed integer == int typedef unsigned int ImU32; // 32-bit unsigned integer (often used to store packed colors) -#if defined(_MSC_VER) && !defined(__clang__) -typedef signed __int64 ImS64; // 64-bit signed integer (pre and post C++11 with Visual Studio) -typedef unsigned __int64 ImU64; // 64-bit unsigned integer (pre and post C++11 with Visual Studio) -#elif (defined(__clang__) || defined(__GNUC__)) && (__cplusplus < 201100) -#include -typedef int64_t ImS64; // 64-bit signed integer (pre C++11) -typedef uint64_t ImU64; // 64-bit unsigned integer (pre C++11) +typedef signed long long ImS64; // 64-bit signed integer +typedef unsigned long long ImU64; // 64-bit unsigned integer + +// Character types +// (we generally use UTF-8 encoded string in the API. This is storage specifically for a decoded character used for keyboard input and display) +typedef unsigned short ImWchar16; // A single decoded U16 character/code point. We encode them as multi bytes UTF-8 when used in strings. +typedef unsigned int ImWchar32; // A single decoded U32 character/code point. We encode them as multi bytes UTF-8 when used in strings. +#ifdef IMGUI_USE_WCHAR32 // ImWchar [configurable type: override in imconfig.h with '#define IMGUI_USE_WCHAR32' to support Unicode planes 1-16] +typedef ImWchar32 ImWchar; #else -typedef signed long long ImS64; // 64-bit signed integer (post C++11) -typedef unsigned long long ImU64; // 64-bit unsigned integer (post C++11) +typedef ImWchar16 ImWchar; #endif -// 2D vector (often used to store positions or sizes) +// Callback and functions types +typedef int (*ImGuiInputTextCallback)(ImGuiInputTextCallbackData* data); // Callback function for ImGui::InputText() +typedef void (*ImGuiSizeCallback)(ImGuiSizeCallbackData* data); // Callback function for ImGui::SetNextWindowSizeConstraints() +typedef void* (*ImGuiMemAllocFunc)(size_t sz, void* user_data); // Function signature for ImGui::SetAllocatorFunctions() +typedef void (*ImGuiMemFreeFunc)(void* ptr, void* user_data); // Function signature for ImGui::SetAllocatorFunctions() + +// ImVec2: 2D vector used to store positions, sizes etc. [Compile-time configurable type] +// This is a frequently used type in the API. Consider using IM_VEC2_CLASS_EXTRA to create implicit cast from/to our preferred type. +IM_MSVC_RUNTIME_CHECKS_OFF struct ImVec2 { float x, y; @@ -232,7 +258,7 @@ struct ImVec2 #endif }; -// 4D vector (often used to store floating-point colors) +// ImVec4: 4D vector used to store clipping rectangles, colors etc. [Compile-time configurable type] struct ImVec4 { float x, y, z, w; @@ -242,6 +268,7 @@ struct ImVec4 IM_VEC4_CLASS_EXTRA // Define additional constructors and implicit cast operators in imconfig.h to convert back and forth between your math types and ImVec4. #endif }; +IM_MSVC_RUNTIME_CHECKS_RESTORE //----------------------------------------------------------------------------- // [SECTION] Dear ImGui end-user API functions @@ -251,8 +278,9 @@ struct ImVec4 namespace ImGui { // Context creation and access - // Each context create its own ImFontAtlas by default. You may instance one yourself and pass it to CreateContext() to share a font atlas between imgui contexts. - // None of those functions is reliant on the current context. + // - Each context create its own ImFontAtlas by default. You may instance one yourself and pass it to CreateContext() to share a font atlas between contexts. + // - DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions() + // for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for details. IMGUI_API ImGuiContext* CreateContext(ImFontAtlas* shared_font_atlas = NULL); IMGUI_API void DestroyContext(ImGuiContext* ctx = NULL); // NULL = destroy current context IMGUI_API ImGuiContext* GetCurrentContext(); @@ -269,6 +297,7 @@ namespace ImGui // Demo, Debug, Information IMGUI_API void ShowDemoWindow(bool* p_open = NULL); // create Demo window. demonstrate most ImGui features. call this to learn about the library! try to make it always available in your application! IMGUI_API void ShowMetricsWindow(bool* p_open = NULL); // create Metrics/Debugger window. display Dear ImGui internals: windows, draw commands, various internal state, etc. + IMGUI_API void ShowStackToolWindow(bool* p_open = NULL); // create Stack Tool window. hover items with mouse to query information about the source of their unique ID. IMGUI_API void ShowAboutWindow(bool* p_open = NULL); // create About window. display Dear ImGui version, credits and build/system information. IMGUI_API void ShowStyleEditor(ImGuiStyle* ref = NULL); // add style editor block (not a window). you can pass in a reference ImGuiStyle structure to compare to, revert to and save to (else it uses the default style) IMGUI_API bool ShowStyleSelector(const char* label); // add style selector block (not a window), essentially a combo listing the default styles. @@ -320,7 +349,8 @@ namespace ImGui IMGUI_API float GetWindowWidth(); // get current window width (shortcut for GetWindowSize().x) IMGUI_API float GetWindowHeight(); // get current window height (shortcut for GetWindowSize().y) - // Prefer using SetNextXXX functions (before Begin) rather that SetXXX functions (after Begin). + // Window manipulation + // - Prefer using SetNextXXX functions (before Begin) rather that SetXXX functions (after Begin). IMGUI_API void SetNextWindowPos(const ImVec2& pos, ImGuiCond cond = 0, const ImVec2& pivot = ImVec2(0, 0)); // set next window position. call before Begin(). use pivot=(0.5f,0.5f) to center on given point, etc. IMGUI_API void SetNextWindowSize(const ImVec2& size, ImGuiCond cond = 0); // set next window size. set axis to 0.0f to force an auto-fit on this axis. call before Begin() IMGUI_API void SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback = NULL, void* custom_callback_data = NULL); // set next window size limits. use -1,-1 on either X/Y axis to preserve the current size. Sizes will be rounded down. Use callback to apply non-trivial programmatic constraints. @@ -332,7 +362,7 @@ namespace ImGui IMGUI_API void SetWindowSize(const ImVec2& size, ImGuiCond cond = 0); // (not recommended) set current window size - call within Begin()/End(). set to ImVec2(0, 0) to force an auto-fit. prefer using SetNextWindowSize(), as this may incur tearing and minor side-effects. IMGUI_API void SetWindowCollapsed(bool collapsed, ImGuiCond cond = 0); // (not recommended) set current window collapsed state. prefer using SetNextWindowCollapsed(). IMGUI_API void SetWindowFocus(); // (not recommended) set current window to be focused / top-most. prefer using SetNextWindowFocus(). - IMGUI_API void SetWindowFontScale(float scale); // set font scale. Adjust IO.FontGlobalScale if you want to scale all windows. This is an old API! For correct scaling, prefer to reload font + rebuild ImFontAtlas + call style.ScaleAllSizes(). + IMGUI_API void SetWindowFontScale(float scale); // [OBSOLETE] set font scale. Adjust IO.FontGlobalScale if you want to scale all windows. This is an old API! For correct scaling, prefer to reload font + rebuild ImFontAtlas + call style.ScaleAllSizes(). IMGUI_API void SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond = 0); // set named window position. IMGUI_API void SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond = 0); // set named window size. set axis to 0.0f to force an auto-fit on this axis. IMGUI_API void SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond = 0); // set named window collapsed state @@ -343,9 +373,8 @@ namespace ImGui // - Those functions are bound to be redesigned (they are confusing, incomplete and the Min/Max return values are in local window coordinates which increases confusion) IMGUI_API ImVec2 GetContentRegionAvail(); // == GetContentRegionMax() - GetCursorPos() IMGUI_API ImVec2 GetContentRegionMax(); // current content boundaries (typically window boundaries including scrolling, or current column boundaries), in windows coordinates - IMGUI_API ImVec2 GetWindowContentRegionMin(); // content boundaries min (roughly (0,0)-Scroll), in window coordinates - IMGUI_API ImVec2 GetWindowContentRegionMax(); // content boundaries max (roughly (0,0)+Size-Scroll) where Size can be override with SetNextWindowContentSize(), in window coordinates - IMGUI_API float GetWindowContentRegionWidth(); // + IMGUI_API ImVec2 GetWindowContentRegionMin(); // content boundaries min for the full window (roughly (0,0)-Scroll), in window coordinates + IMGUI_API ImVec2 GetWindowContentRegionMax(); // content boundaries max for the full window (roughly (0,0)+Size-Scroll) where Size can be override with SetNextWindowContentSize(), in window coordinates // Windows Scrolling IMGUI_API float GetScrollX(); // get scrolling amount [0 .. GetScrollMaxX()] @@ -368,13 +397,13 @@ namespace ImGui IMGUI_API void PushStyleVar(ImGuiStyleVar idx, float val); // modify a style float variable. always use this if you modify the style after NewFrame(). IMGUI_API void PushStyleVar(ImGuiStyleVar idx, const ImVec2& val); // modify a style ImVec2 variable. always use this if you modify the style after NewFrame(). IMGUI_API void PopStyleVar(int count = 1); - IMGUI_API void PushAllowKeyboardFocus(bool allow_keyboard_focus); // allow focusing using TAB/Shift-TAB, enabled by default but you can disable it for certain widgets + IMGUI_API void PushAllowKeyboardFocus(bool allow_keyboard_focus); // == tab stop enable. Allow focusing using TAB/Shift-TAB, enabled by default but you can disable it for certain widgets IMGUI_API void PopAllowKeyboardFocus(); IMGUI_API void PushButtonRepeat(bool repeat); // in 'repeat' mode, Button*() functions return repeated true in a typematic manner (using io.KeyRepeatDelay/io.KeyRepeatRate setting). Note that you can call IsItemActive() after any Button() to tell if the button is held in the current frame. IMGUI_API void PopButtonRepeat(); // Parameters stacks (current window) - IMGUI_API void PushItemWidth(float item_width); // push width of items for common large "item+label" widgets. >0.0f: width in pixels, <0.0f align xx pixels to the right of window (so -FLT_MIN always align width to the right side). 0.0f = default to ~2/3 of windows width, + IMGUI_API void PushItemWidth(float item_width); // push width of items for common large "item+label" widgets. >0.0f: width in pixels, <0.0f align xx pixels to the right of window (so -FLT_MIN always align width to the right side). IMGUI_API void PopItemWidth(); IMGUI_API void SetNextItemWidth(float item_width); // set width of the _next_ common large "item+label" widget. >0.0f: width in pixels, <0.0f align xx pixels to the right of window (so -FLT_MIN always align width to the right side) IMGUI_API float CalcItemWidth(); // width of item given pushed settings and current cursor position. NOT necessarily the width of last item unlike most 'Item' functions. @@ -382,6 +411,7 @@ namespace ImGui IMGUI_API void PopTextWrapPos(); // Style read access + // - Use the style editor (ShowStyleEditor() function) to interactively see what the colors are) IMGUI_API ImFont* GetFont(); // get current font IMGUI_API float GetFontSize(); // get current font size (= height in pixels) of current font with current scale applied IMGUI_API ImVec2 GetFontTexUvWhitePixel(); // get UV coordinate for a while pixel, useful to draw custom shapes via the ImDrawList API @@ -413,8 +443,8 @@ namespace ImGui IMGUI_API void SetCursorPosX(float local_x); // GetWindowPos() + GetCursorPos() == GetCursorScreenPos() etc.) IMGUI_API void SetCursorPosY(float local_y); // IMGUI_API ImVec2 GetCursorStartPos(); // initial cursor position in window coordinates - IMGUI_API ImVec2 GetCursorScreenPos(); // cursor position in absolute screen coordinates [0..io.DisplaySize] (useful to work with ImDrawList API) - IMGUI_API void SetCursorScreenPos(const ImVec2& pos); // cursor position in absolute screen coordinates [0..io.DisplaySize] + IMGUI_API ImVec2 GetCursorScreenPos(); // cursor position in absolute coordinates (useful to work with ImDrawList API). generally top-left == GetMainViewport()->Pos == (0,0) in single viewport mode, and bottom-right == GetMainViewport()->Pos+Size == io.DisplaySize in single-viewport mode. + IMGUI_API void SetCursorScreenPos(const ImVec2& pos); // cursor position in absolute coordinates IMGUI_API void AlignTextToFramePadding(); // vertically align upcoming text baseline to FramePadding.y so that it will align properly to regularly framed items (call if you have text on a line before a framed item) IMGUI_API float GetTextLineHeight(); // ~ FontSize IMGUI_API float GetTextLineHeightWithSpacing(); // ~ FontSize + style.ItemSpacing.y (distance in pixels between 2 consecutive lines of text) @@ -422,11 +452,15 @@ namespace ImGui IMGUI_API float GetFrameHeightWithSpacing(); // ~ FontSize + style.FramePadding.y * 2 + style.ItemSpacing.y (distance in pixels between 2 consecutive lines of framed widgets) // ID stack/scopes - // - Read the FAQ for more details about how ID are handled in dear imgui. If you are creating widgets in a loop you most - // likely want to push a unique identifier (e.g. object pointer, loop index) to uniquely differentiate them. - // - The resulting ID are hashes of the entire stack. + // Read the FAQ (docs/FAQ.md or http://dearimgui.org/faq) for more details about how ID are handled in dear imgui. + // - Those questions are answered and impacted by understanding of the ID stack system: + // - "Q: Why is my widget not reacting when I click on it?" + // - "Q: How can I have widgets with an empty label?" + // - "Q: How can I have multiple widgets with the same label?" + // - Short version: ID are hashes of the entire ID stack. If you are creating widgets in a loop you most likely + // want to push a unique identifier (e.g. object pointer, loop index) to uniquely differentiate them. // - You can also use the "Label##foobar" syntax within widget label to distinguish them from each others. - // - In this header file we use the "label"/"name" terminology to denote a string that will be displayed and used as an ID, + // - In this header file we use the "label"/"name" terminology to denote a string that will be displayed + used as an ID, // whereas "str_id" denote a string that is only used as an ID and not normally displayed. IMGUI_API void PushID(const char* str_id); // push string into the ID stack (will hash string). IMGUI_API void PushID(const char* str_id_begin, const char* str_id_end); // push string into the ID stack (will hash string). @@ -471,7 +505,7 @@ namespace ImGui // Widgets: Combo Box // - The BeginCombo()/EndCombo() api allows you to manage your contents and selection state however you want it, by creating e.g. Selectable() items. - // - The old Combo() api are helpers over BeginCombo()/EndCombo() which are kept available for convenience purpose. + // - The old Combo() api are helpers over BeginCombo()/EndCombo() which are kept available for convenience purpose. This is analogous to how ListBox are created. IMGUI_API bool BeginCombo(const char* label, const char* preview_value, ImGuiComboFlags flags = 0); IMGUI_API void EndCombo(); // only call EndCombo() if BeginCombo() returns true! IMGUI_API bool Combo(const char* label, int* current_item, const char* const items[], int items_count, int popup_max_height_in_items = -1); @@ -479,12 +513,13 @@ namespace ImGui IMGUI_API bool Combo(const char* label, int* current_item, bool(*items_getter)(void* data, int idx, const char** out_text), void* data, int items_count, int popup_max_height_in_items = -1); // Widgets: Drag Sliders - // - CTRL+Click on any drag box to turn them into an input box. Manually input values aren't clamped and can go off-bounds. - // - For all the Float2/Float3/Float4/Int2/Int3/Int4 versions of every functions, note that a 'float v[X]' function argument is the same as 'float* v', the array syntax is just a way to document the number of elements that are expected to be accessible. You can pass address of your first element out of a contiguous set, e.g. &myvector.x + // - CTRL+Click on any drag box to turn them into an input box. Manually input values aren't clamped by default and can go off-bounds. Use ImGuiSliderFlags_AlwaysClamp to always clamp. + // - For all the Float2/Float3/Float4/Int2/Int3/Int4 versions of every functions, note that a 'float v[X]' function argument is the same as 'float* v', + // the array syntax is just a way to document the number of elements that are expected to be accessible. You can pass address of your first element out of a contiguous set, e.g. &myvector.x // - Adjust format string to decorate the value with a prefix, a suffix, or adapt the editing and display precision e.g. "%.3f" -> 1.234; "%5.2f secs" -> 01.23 secs; "Biscuit: %.0f" -> Biscuit: 1; etc. // - Format string may also be set to NULL or use the default format ("%f" or "%d"). // - Speed are per-pixel of mouse movement (v_speed=0.2f: mouse needs to move by 5 pixels to increase value by 1). For gamepad/keyboard navigation, minimum speed is Max(v_speed, minimum_step_at_given_precision). - // - Use v_min < v_max to clamp edits to given limits. Note that CTRL+Click manual input can override those limits. + // - Use v_min < v_max to clamp edits to given limits. Note that CTRL+Click manual input can override those limits if ImGuiSliderFlags_AlwaysClamp is not used. // - Use v_max = FLT_MAX / INT_MAX etc to avoid clamping to a maximum, same with v_min = -FLT_MAX / INT_MIN to avoid clamping to a minimum. // - We use the same sets of flags for DragXXX() and SliderXXX() functions as the features are the same and it makes it easier to swap them. // - Legacy: Pre-1.78 there are DragXXX() function signatures that takes a final `float power=1.0f' argument instead of the `ImGuiSliderFlags flags=0' argument. @@ -499,11 +534,11 @@ namespace ImGui IMGUI_API bool DragInt3(const char* label, int v[3], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = "%d", ImGuiSliderFlags flags = 0); IMGUI_API bool DragInt4(const char* label, int v[4], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = "%d", ImGuiSliderFlags flags = 0); IMGUI_API bool DragIntRange2(const char* label, int* v_current_min, int* v_current_max, float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = "%d", const char* format_max = NULL, ImGuiSliderFlags flags = 0); - IMGUI_API bool DragScalar(const char* label, ImGuiDataType data_type, void* p_data, float v_speed, const void* p_min = NULL, const void* p_max = NULL, const char* format = NULL, ImGuiSliderFlags flags = 0); - IMGUI_API bool DragScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, float v_speed, const void* p_min = NULL, const void* p_max = NULL, const char* format = NULL, ImGuiSliderFlags flags = 0); + IMGUI_API bool DragScalar(const char* label, ImGuiDataType data_type, void* p_data, float v_speed = 1.0f, const void* p_min = NULL, const void* p_max = NULL, const char* format = NULL, ImGuiSliderFlags flags = 0); + IMGUI_API bool DragScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, float v_speed = 1.0f, const void* p_min = NULL, const void* p_max = NULL, const char* format = NULL, ImGuiSliderFlags flags = 0); // Widgets: Regular Sliders - // - CTRL+Click on any slider to turn them into an input box. Manually input values aren't clamped and can go off-bounds. + // - CTRL+Click on any slider to turn them into an input box. Manually input values aren't clamped by default and can go off-bounds. Use ImGuiSliderFlags_AlwaysClamp to always clamp. // - Adjust format string to decorate the value with a prefix, a suffix, or adapt the editing and display precision e.g. "%.3f" -> 1.234; "%5.2f secs" -> 01.23 secs; "Biscuit: %.0f" -> Biscuit: 1; etc. // - Format string may also be set to NULL or use the default format ("%f" or "%d"). // - Legacy: Pre-1.78 there are SliderXXX() function signatures that takes a final `float power=1.0f' argument instead of the `ImGuiSliderFlags flags=0' argument. @@ -578,14 +613,18 @@ namespace ImGui IMGUI_API bool Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags = 0, const ImVec2& size = ImVec2(0, 0)); // "bool* p_selected" point to the selection state (read-write), as a convenient helper. // Widgets: List Boxes - // - FIXME: To be consistent with all the newer API, ListBoxHeader/ListBoxFooter should in reality be called BeginListBox/EndListBox. Will rename them. + // - This is essentially a thin wrapper to using BeginChild/EndChild with some stylistic changes. + // - The BeginListBox()/EndListBox() api allows you to manage your contents and selection state however you want it, by creating e.g. Selectable() or any items. + // - The simplified/old ListBox() api are helpers over BeginListBox()/EndListBox() which are kept available for convenience purpose. This is analoguous to how Combos are created. + // - Choose frame width: size.x > 0.0f: custom / size.x < 0.0f or -FLT_MIN: right-align / size.x = 0.0f (default): use current ItemWidth + // - Choose frame height: size.y > 0.0f: custom / size.y < 0.0f or -FLT_MIN: bottom-align / size.y = 0.0f (default): arbitrary default height which can fit ~7 items + IMGUI_API bool BeginListBox(const char* label, const ImVec2& size = ImVec2(0, 0)); // open a framed scrolling region + IMGUI_API void EndListBox(); // only call EndListBox() if BeginListBox() returned true! IMGUI_API bool ListBox(const char* label, int* current_item, const char* const items[], int items_count, int height_in_items = -1); IMGUI_API bool ListBox(const char* label, int* current_item, bool (*items_getter)(void* data, int idx, const char** out_text), void* data, int items_count, int height_in_items = -1); - IMGUI_API bool ListBoxHeader(const char* label, const ImVec2& size = ImVec2(0, 0)); // use if you want to reimplement ListBox() will custom data or interactions. if the function return true, you can output elements then call ListBoxFooter() afterwards. - IMGUI_API bool ListBoxHeader(const char* label, int items_count, int height_in_items = -1); // " - IMGUI_API void ListBoxFooter(); // terminate the scrolling region. only call ListBoxFooter() if ListBoxHeader() returned true! // Widgets: Data Plotting + // - Consider using ImPlot (https://github.com/epezent/implot) which is much better! IMGUI_API void PlotLines(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 0), int stride = sizeof(float)); IMGUI_API void PlotLines(const char* label, float(*values_getter)(void* data, int idx), void* data, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 0)); IMGUI_API void PlotHistogram(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 0), int stride = sizeof(float)); @@ -602,13 +641,14 @@ namespace ImGui // - Use BeginMenuBar() on a window ImGuiWindowFlags_MenuBar to append to its menu bar. // - Use BeginMainMenuBar() to create a menu bar at the top of the screen and append to it. // - Use BeginMenu() to create a menu. You can call BeginMenu() multiple time with the same identifier to append more items to it. + // - Not that MenuItem() keyboardshortcuts are displayed as a convenience but _not processed_ by Dear ImGui at the moment. IMGUI_API bool BeginMenuBar(); // append to menu-bar of current window (requires ImGuiWindowFlags_MenuBar flag set on parent window). IMGUI_API void EndMenuBar(); // only call EndMenuBar() if BeginMenuBar() returns true! IMGUI_API bool BeginMainMenuBar(); // create and append to a full screen menu-bar. IMGUI_API void EndMainMenuBar(); // only call EndMainMenuBar() if BeginMainMenuBar() returns true! IMGUI_API bool BeginMenu(const char* label, bool enabled = true); // create a sub-menu entry. only call EndMenu() if this returns true! IMGUI_API void EndMenu(); // only call EndMenu() if BeginMenu() returns true! - IMGUI_API bool MenuItem(const char* label, const char* shortcut = NULL, bool selected = false, bool enabled = true); // return true when activated. shortcuts are displayed for convenience but not processed by ImGui at the moment + IMGUI_API bool MenuItem(const char* label, const char* shortcut = NULL, bool selected = false, bool enabled = true); // return true when activated. IMGUI_API bool MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled = true); // return true when activated + toggle (*p_selected) if p_selected != NULL // Tooltips @@ -626,37 +666,43 @@ namespace ImGui // - You can bypass the hovering restriction by using ImGuiHoveredFlags_AllowWhenBlockedByPopup when calling IsItemHovered() or IsWindowHovered(). // - IMPORTANT: Popup identifiers are relative to the current ID stack, so OpenPopup and BeginPopup generally needs to be at the same level of the stack. // This is sometimes leading to confusing mistakes. May rework this in the future. + // Popups: begin/end functions // - BeginPopup(): query popup state, if open start appending into the window. Call EndPopup() afterwards. ImGuiWindowFlags are forwarded to the window. // - BeginPopupModal(): block every interactions behind the window, cannot be closed by user, add a dimming background, has a title bar. IMGUI_API bool BeginPopup(const char* str_id, ImGuiWindowFlags flags = 0); // return true if the popup is open, and you can start outputting to it. IMGUI_API bool BeginPopupModal(const char* name, bool* p_open = NULL, ImGuiWindowFlags flags = 0); // return true if the modal is open, and you can start outputting to it. IMGUI_API void EndPopup(); // only call EndPopup() if BeginPopupXXX() returns true! + // Popups: open/close functions // - OpenPopup(): set popup state to open. ImGuiPopupFlags are available for opening options. // - If not modal: they can be closed by clicking anywhere outside them, or by pressing ESCAPE. // - CloseCurrentPopup(): use inside the BeginPopup()/EndPopup() scope to close manually. // - CloseCurrentPopup() is called by default by Selectable()/MenuItem() when activated (FIXME: need some options). // - Use ImGuiPopupFlags_NoOpenOverExistingPopup to avoid opening a popup if there's already one at the same level. This is equivalent to e.g. testing for !IsAnyPopupOpen() prior to OpenPopup(). + // - Use IsWindowAppearing() after BeginPopup() to tell if a window just opened. + // - IMPORTANT: Notice that for OpenPopupOnItemClick() we exceptionally default flags to 1 (== ImGuiPopupFlags_MouseButtonRight) for backward compatibility with older API taking 'int mouse_button = 1' parameter IMGUI_API void OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags = 0); // call to mark popup as open (don't call every frame!). - IMGUI_API void OpenPopupOnItemClick(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1); // helper to open popup when clicked on last item. return true when just opened. (note: actually triggers on the mouse _released_ event to be consistent with popup behaviors) + IMGUI_API void OpenPopup(ImGuiID id, ImGuiPopupFlags popup_flags = 0); // id overload to facilitate calling from nested stacks + IMGUI_API void OpenPopupOnItemClick(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1); // helper to open popup when clicked on last item. Default to ImGuiPopupFlags_MouseButtonRight == 1. (note: actually triggers on the mouse _released_ event to be consistent with popup behaviors) IMGUI_API void CloseCurrentPopup(); // manually close the popup we have begin-ed into. + // Popups: open+begin combined functions helpers // - Helpers to do OpenPopup+BeginPopup where the Open action is triggered by e.g. hovering an item and right-clicking. // - They are convenient to easily create context menus, hence the name. // - IMPORTANT: Notice that BeginPopupContextXXX takes ImGuiPopupFlags just like OpenPopup() and unlike BeginPopup(). For full consistency, we may add ImGuiWindowFlags to the BeginPopupContextXXX functions in the future. - // - IMPORTANT: we exceptionally default their flags to 1 (== ImGuiPopupFlags_MouseButtonRight) for backward compatibility with older API taking 'int mouse_button = 1' parameter, so if you add other flags remember to re-add the ImGuiPopupFlags_MouseButtonRight. - IMGUI_API bool BeginPopupContextItem(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1); // open+begin popup when clicked on last item. if you can pass a NULL str_id only if the previous item had an id. If you want to use that on a non-interactive item such as Text() you need to pass in an explicit ID here. read comments in .cpp! + // - IMPORTANT: Notice that we exceptionally default their flags to 1 (== ImGuiPopupFlags_MouseButtonRight) for backward compatibility with older API taking 'int mouse_button = 1' parameter, so if you add other flags remember to re-add the ImGuiPopupFlags_MouseButtonRight. + IMGUI_API bool BeginPopupContextItem(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1); // open+begin popup when clicked on last item. Use str_id==NULL to associate the popup to previous item. If you want to use that on a non-interactive item such as Text() you need to pass in an explicit ID here. read comments in .cpp! IMGUI_API bool BeginPopupContextWindow(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1);// open+begin popup when clicked on current window. IMGUI_API bool BeginPopupContextVoid(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1); // open+begin popup when clicked in void (where there are no windows). - // Popups: test function + + // Popups: query functions // - IsPopupOpen(): return true if the popup is open at the current BeginPopup() level of the popup stack. // - IsPopupOpen() with ImGuiPopupFlags_AnyPopupId: return true if any popup is open at the current BeginPopup() level of the popup stack. // - IsPopupOpen() with ImGuiPopupFlags_AnyPopupId + ImGuiPopupFlags_AnyPopupLevel: return true if any popup is open. IMGUI_API bool IsPopupOpen(const char* str_id, ImGuiPopupFlags flags = 0); // return true if the popup is open. // Tables - // [BETA API] API may evolve slightly! If you use this, please update to the next version when it comes out! // - Full-featured replacement for old Columns API. // - See Demo->Tables for demo code. // - See top of imgui_tables.cpp for general commentary. @@ -685,6 +731,7 @@ namespace ImGui IMGUI_API void TableNextRow(ImGuiTableRowFlags row_flags = 0, float min_row_height = 0.0f); // append into the first cell of a new row. IMGUI_API bool TableNextColumn(); // append into the next column (or first column of next row if currently in last column). Return true when column is visible. IMGUI_API bool TableSetColumnIndex(int column_n); // append into the specified column. Return true when column is visible. + // Tables: Headers & Columns declaration // - Use TableSetupColumn() to specify label, resizing policy, default width/weight, id, various other flags etc. // - Use TableHeadersRow() to create a header row and automatically submit a TableHeader() for each column. @@ -693,17 +740,19 @@ namespace ImGui // - You may manually submit headers using TableNextRow() + TableHeader() calls, but this is only useful in // some advanced use cases (e.g. adding custom widgets in header row). // - Use TableSetupScrollFreeze() to lock columns/rows so they stay visible when scrolled. - IMGUI_API void TableSetupColumn(const char* label, ImGuiTableColumnFlags flags = 0, float init_width_or_weight = 0.0f, ImU32 user_id = 0); + IMGUI_API void TableSetupColumn(const char* label, ImGuiTableColumnFlags flags = 0, float init_width_or_weight = 0.0f, ImGuiID user_id = 0); IMGUI_API void TableSetupScrollFreeze(int cols, int rows); // lock columns/rows so they stay visible when scrolled. IMGUI_API void TableHeadersRow(); // submit all headers cells based on data provided to TableSetupColumn() + submit context menu IMGUI_API void TableHeader(const char* label); // submit one header cell manually (rarely used) + // Tables: Sorting // - Call TableGetSortSpecs() to retrieve latest sort specs for the table. NULL when not sorting. // - When 'SpecsDirty == true' you should sort your data. It will be true when sorting specs have changed // since last call, or the first time. Make sure to set 'SpecsDirty = false' after sorting, else you may // wastefully sort your data every frame! // - Lifetime: don't hold on this pointer over multiple frames or past any subsequent call to BeginTable(). - IMGUI_API ImGuiTableSortSpecs* TableGetSortSpecs(); // get latest sort specs for the table (NULL if not sorting). + IMGUI_API ImGuiTableSortSpecs* TableGetSortSpecs(); // get latest sort specs for the table (NULL if not sorting). + // Tables: Miscellaneous functions // - Functions args 'int column_n' treat the default value of -1 as the same as passing the current column index. IMGUI_API int TableGetColumnCount(); // return number of columns (value passed to BeginTable) @@ -711,9 +760,10 @@ namespace ImGui IMGUI_API int TableGetRowIndex(); // return current row index. IMGUI_API const char* TableGetColumnName(int column_n = -1); // return "" if column didn't have a name declared by TableSetupColumn(). Pass -1 to use current column. IMGUI_API ImGuiTableColumnFlags TableGetColumnFlags(int column_n = -1); // return column flags so you can query their Enabled/Visible/Sorted/Hovered status flags. Pass -1 to use current column. + IMGUI_API void TableSetColumnEnabled(int column_n, bool v);// change user accessible enabled/disabled state of a column. Set to false to hide the column. User can use the context menu to change this themselves (right-click in headers, or right-click in columns body with ImGuiTableFlags_ContextMenuInBody) IMGUI_API void TableSetBgColor(ImGuiTableBgTarget target, ImU32 color, int column_n = -1); // change the color of a cell, row, or column. See ImGuiTableBgTarget_ flags for details. - // Legacy Columns API (2020: prefer using Tables!) + // Legacy Columns API (prefer using Tables!) // - You can also use SameLine(pos_x) to mimic simplified columns. IMGUI_API void Columns(int count = 1, const char* id = NULL, bool border = true); IMGUI_API void NextColumn(); // next column, defaults to current row or next row if the current row is finished @@ -740,17 +790,28 @@ namespace ImGui IMGUI_API void LogFinish(); // stop logging (close file, etc.) IMGUI_API void LogButtons(); // helper to display buttons for logging to tty/file/clipboard IMGUI_API void LogText(const char* fmt, ...) IM_FMTARGS(1); // pass text data straight to log (without being displayed) + IMGUI_API void LogTextV(const char* fmt, va_list args) IM_FMTLIST(1); // Drag and Drop - // - If you stop calling BeginDragDropSource() the payload is preserved however it won't have a preview tooltip (we currently display a fallback "..." tooltip as replacement) - IMGUI_API bool BeginDragDropSource(ImGuiDragDropFlags flags = 0); // call when the current item is active. If this return true, you can call SetDragDropPayload() + EndDragDropSource() - IMGUI_API bool SetDragDropPayload(const char* type, const void* data, size_t sz, ImGuiCond cond = 0); // type is a user defined string of maximum 32 characters. Strings starting with '_' are reserved for dear imgui internal types. Data is copied and held by imgui. + // - On source items, call BeginDragDropSource(), if it returns true also call SetDragDropPayload() + EndDragDropSource(). + // - On target candidates, call BeginDragDropTarget(), if it returns true also call AcceptDragDropPayload() + EndDragDropTarget(). + // - If you stop calling BeginDragDropSource() the payload is preserved however it won't have a preview tooltip (we currently display a fallback "..." tooltip, see #1725) + // - An item can be both drag source and drop target. + IMGUI_API bool BeginDragDropSource(ImGuiDragDropFlags flags = 0); // call after submitting an item which may be dragged. when this return true, you can call SetDragDropPayload() + EndDragDropSource() + IMGUI_API bool SetDragDropPayload(const char* type, const void* data, size_t sz, ImGuiCond cond = 0); // type is a user defined string of maximum 32 characters. Strings starting with '_' are reserved for dear imgui internal types. Data is copied and held by imgui. Return true when payload has been accepted. IMGUI_API void EndDragDropSource(); // only call EndDragDropSource() if BeginDragDropSource() returns true! IMGUI_API bool BeginDragDropTarget(); // call after submitting an item that may receive a payload. If this returns true, you can call AcceptDragDropPayload() + EndDragDropTarget() IMGUI_API const ImGuiPayload* AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags = 0); // accept contents of a given type. If ImGuiDragDropFlags_AcceptBeforeDelivery is set you can peek into the payload before the mouse button is released. IMGUI_API void EndDragDropTarget(); // only call EndDragDropTarget() if BeginDragDropTarget() returns true! IMGUI_API const ImGuiPayload* GetDragDropPayload(); // peek directly into the current payload from anywhere. may return NULL. use ImGuiPayload::IsDataType() to test for the payload type. + // Disabling [BETA API] + // - Disable all user interactions and dim items visuals (applying style.DisabledAlpha over current colors) + // - Those can be nested but it cannot be used to enable an already disabled section (a single BeginDisabled(true) in the stack is enough to keep everything disabled) + // - BeginDisabled(false) essentially does nothing useful but is provided to facilitate use of boolean expressions. If you can avoid calling BeginDisabled(False)/EndDisabled() best to avoid it. + IMGUI_API void BeginDisabled(bool disabled = true); + IMGUI_API void EndDisabled(); + // Clipping // - Mouse hovering is affected by ImGui::PushClipRect() calls, unlike direct calls to ImDrawList::PushClipRect() which are render only. IMGUI_API void PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect); @@ -761,13 +822,13 @@ namespace ImGui IMGUI_API void SetItemDefaultFocus(); // make last item the default focused item of a window. IMGUI_API void SetKeyboardFocusHere(int offset = 0); // focus keyboard on the next widget. Use positive 'offset' to access sub components of a multiple component widget. Use -1 to access previous widget. - // Item/Widgets Utilities - // - Most of the functions are referring to the last/previous item we submitted. + // Item/Widgets Utilities and Query Functions + // - Most of the functions are referring to the previous Item that has been submitted. // - See Demo Window under "Widgets->Querying Status" for an interactive visualization of most of those functions. IMGUI_API bool IsItemHovered(ImGuiHoveredFlags flags = 0); // is the last item hovered? (and usable, aka not blocked by a popup, etc.). See ImGuiHoveredFlags for more options. IMGUI_API bool IsItemActive(); // is the last item active? (e.g. button being held, text field being edited. This will continuously return true while holding mouse button on an item. Items that don't interact will always return false) IMGUI_API bool IsItemFocused(); // is the last item focused for keyboard/gamepad navigation? - IMGUI_API bool IsItemClicked(ImGuiMouseButton mouse_button = 0); // is the last item clicked? (e.g. button/node just clicked on) == IsMouseClicked(mouse_button) && IsItemHovered() + IMGUI_API bool IsItemClicked(ImGuiMouseButton mouse_button = 0); // is the last item hovered and mouse clicked on? (**) == IsMouseClicked(mouse_button) && IsItemHovered()Important. (**) this it NOT equivalent to the behavior of e.g. Button(). Read comments in function definition. IMGUI_API bool IsItemVisible(); // is the last item visible? (items may be out of sight because of clipping/scrolling) IMGUI_API bool IsItemEdited(); // did the last item modify its underlying value this frame? or was pressed? This is generally the same as the "bool" return value of many widgets. IMGUI_API bool IsItemActivated(); // was the last item just made active (item was previously inactive). @@ -782,6 +843,12 @@ namespace ImGui IMGUI_API ImVec2 GetItemRectSize(); // get size of last item IMGUI_API void SetItemAllowOverlap(); // allow last item to be overlapped by a subsequent item. sometimes useful with invisible buttons, selectables, etc. to catch unused area. + // Viewports + // - Currently represents the Platform Window created by the application which is hosting our Dear ImGui windows. + // - In 'docking' branch with multi-viewport enabled, we extend this concept to have multiple active viewports. + // - In the future we will extend this concept further to also represent Platform Monitor and support a "no main platform window" operation mode. + IMGUI_API ImGuiViewport* GetMainViewport(); // return primary/default viewport. This can never be NULL. + // Miscellaneous Utilities IMGUI_API bool IsRectVisible(const ImVec2& size); // test if rectangle (of given size, starting from cursor position) is visible / not clipped. IMGUI_API bool IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max); // test if rectangle (in screen space) is visible / not clipped. to perform coarse clipping on user's side. @@ -793,7 +860,6 @@ namespace ImGui IMGUI_API const char* GetStyleColorName(ImGuiCol idx); // get a string corresponding to the enum value (for display, saving, etc.). IMGUI_API void SetStateStorage(ImGuiStorage* storage); // replace current window storage with our own (if you want to manipulate it yourself, typically clear subsection of it) IMGUI_API ImGuiStorage* GetStateStorage(); - IMGUI_API void CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end); // calculate coarse clipping for large list of evenly sized items. Prefer using the ImGuiListClipper higher-level helper if you can. IMGUI_API bool BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags flags = 0); // helper to create a child window / scrolling region that looks like a normal widget frame IMGUI_API void EndChildFrame(); // always call EndChildFrame() regardless of BeginChildFrame() return values (which indicates a collapsed/clipped window) @@ -807,13 +873,16 @@ namespace ImGui IMGUI_API void ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b); // Inputs Utilities: Keyboard - // - For 'int user_key_index' you can use your own indices/enums according to how your backend/engine stored them in io.KeysDown[]. - // - We don't know the meaning of those value. You can use GetKeyIndex() to map a ImGuiKey_ value into the user index. - IMGUI_API int GetKeyIndex(ImGuiKey imgui_key); // map ImGuiKey_* values into user's key index. == io.KeyMap[key] - IMGUI_API bool IsKeyDown(int user_key_index); // is key being held. == io.KeysDown[user_key_index]. - IMGUI_API bool IsKeyPressed(int user_key_index, bool repeat = true); // was key pressed (went from !Down to Down)? if repeat=true, uses io.KeyRepeatDelay / KeyRepeatRate - IMGUI_API bool IsKeyReleased(int user_key_index); // was key released (went from Down to !Down)? - IMGUI_API int GetKeyPressedAmount(int key_index, float repeat_delay, float rate); // uses provided repeat rate/delay. return a count, most often 0 or 1 but might be >1 if RepeatRate is small enough that DeltaTime > RepeatRate + // Without IMGUI_DISABLE_OBSOLETE_KEYIO: (legacy support) + // - For 'ImGuiKey key' you can still use your legacy native/user indices according to how your backend/engine stored them in io.KeysDown[]. + // With IMGUI_DISABLE_OBSOLETE_KEYIO: (this is the way forward) + // - Any use of 'ImGuiKey' will assert when key < 512 will be passed, previously reserved as native/user keys indices + // - GetKeyIndex() is pass-through and therefore deprecated (gone if IMGUI_DISABLE_OBSOLETE_KEYIO is defined) + IMGUI_API bool IsKeyDown(ImGuiKey key); // is key being held. + IMGUI_API bool IsKeyPressed(ImGuiKey key, bool repeat = true); // was key pressed (went from !Down to Down)? if repeat=true, uses io.KeyRepeatDelay / KeyRepeatRate + IMGUI_API bool IsKeyReleased(ImGuiKey key); // was key released (went from Down to !Down)? + IMGUI_API int GetKeyPressedAmount(ImGuiKey key, float repeat_delay, float rate); // uses provided repeat rate/delay. return a count, most often 0 or 1 but might be >1 if RepeatRate is small enough that DeltaTime > RepeatRate + IMGUI_API const char* GetKeyName(ImGuiKey key); // [DEBUG] returns English name of the key. Those names a provided for debugging purpose and are not meant to be saved persistently not compared. IMGUI_API void CaptureKeyboardFromApp(bool want_capture_keyboard_value = true); // attention: misleading name! manually override io.WantCaptureKeyboard flag next frame (said flag is entirely left for your application to handle). e.g. force capture keyboard when your widget is being hovered. This is equivalent to setting "io.WantCaptureKeyboard = want_capture_keyboard_value"; after the next NewFrame() call. // Inputs Utilities: Mouse @@ -821,12 +890,13 @@ namespace ImGui // - You can also use regular integer: it is forever guaranteed that 0=Left, 1=Right, 2=Middle. // - Dragging operations are only reported after mouse has moved a certain distance away from the initial clicking position (see 'lock_threshold' and 'io.MouseDraggingThreshold') IMGUI_API bool IsMouseDown(ImGuiMouseButton button); // is mouse button held? - IMGUI_API bool IsMouseClicked(ImGuiMouseButton button, bool repeat = false); // did mouse button clicked? (went from !Down to Down) + IMGUI_API bool IsMouseClicked(ImGuiMouseButton button, bool repeat = false); // did mouse button clicked? (went from !Down to Down). Same as GetMouseClickedCount() == 1. IMGUI_API bool IsMouseReleased(ImGuiMouseButton button); // did mouse button released? (went from Down to !Down) - IMGUI_API bool IsMouseDoubleClicked(ImGuiMouseButton button); // did mouse button double-clicked? (note that a double-click will also report IsMouseClicked() == true) + IMGUI_API bool IsMouseDoubleClicked(ImGuiMouseButton button); // did mouse button double-clicked? Same as GetMouseClickedCount() == 2. (note that a double-click will also report IsMouseClicked() == true) + IMGUI_API int GetMouseClickedCount(ImGuiMouseButton button); // return the number of successive mouse-clicks at the time where a click happen (otherwise 0). IMGUI_API bool IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip = true);// is mouse hovering given bounding rect (in screen space). clipped by current clipping settings, but disregarding of other consideration of focus/window ordering/popup-block. IMGUI_API bool IsMousePosValid(const ImVec2* mouse_pos = NULL); // by convention we use (-FLT_MAX,-FLT_MAX) to denote that there is no mouse available - IMGUI_API bool IsAnyMouseDown(); // is any mouse button held? + IMGUI_API bool IsAnyMouseDown(); // [WILL OBSOLETE] is any mouse button held? This was designed for backends, but prefer having backend maintain a mask of held mouse buttons, because upcoming input queue system will make this invalid. IMGUI_API ImVec2 GetMousePos(); // shortcut to ImGui::GetIO().MousePos provided by user, to be consistent with other calls IMGUI_API ImVec2 GetMousePosOnOpeningCurrentPopup(); // retrieve mouse position at the time of opening popup we have BeginPopup() into (helper to avoid user backing that value themselves) IMGUI_API bool IsMouseDragging(ImGuiMouseButton button, float lock_threshold = -1.0f); // is mouse dragging? (if lock_threshold < -1.0f, uses io.MouseDraggingThreshold) @@ -844,18 +914,22 @@ namespace ImGui // Settings/.Ini Utilities // - The disk functions are automatically called if io.IniFilename != NULL (default is "imgui.ini"). // - Set io.IniFilename to NULL to load/save manually. Read io.WantSaveIniSettings description about handling .ini saving manually. + // - Important: default value "imgui.ini" is relative to current working dir! Most apps will want to lock this to an absolute path (e.g. same path as executables). IMGUI_API void LoadIniSettingsFromDisk(const char* ini_filename); // call after CreateContext() and before the first call to NewFrame(). NewFrame() automatically calls LoadIniSettingsFromDisk(io.IniFilename). IMGUI_API void LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size=0); // call after CreateContext() and before the first call to NewFrame() to provide .ini data from your own data source. IMGUI_API void SaveIniSettingsToDisk(const char* ini_filename); // this is automatically called (if io.IniFilename is not empty) a few seconds after any modification that should be reflected in the .ini file (and also by DestroyContext). IMGUI_API const char* SaveIniSettingsToMemory(size_t* out_ini_size = NULL); // return a zero-terminated string with the .ini data which you can save by your own mean. call when io.WantSaveIniSettings is set, then save data by your own mean and clear io.WantSaveIniSettings. // Debug Utilities + // - This is used by the IMGUI_CHECKVERSION() macro. IMGUI_API bool DebugCheckVersionAndDataLayout(const char* version_str, size_t sz_io, size_t sz_style, size_t sz_vec2, size_t sz_vec4, size_t sz_drawvert, size_t sz_drawidx); // This is called by IMGUI_CHECKVERSION() macro. // Memory Allocators - // - All those functions are not reliant on the current context. - // - If you reload the contents of imgui.cpp at runtime, you may need to call SetCurrentContext() + SetAllocatorFunctions() again because we use global storage for those. - IMGUI_API void SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data = NULL); + // - Those functions are not reliant on the current context. + // - DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions() + // for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details. + IMGUI_API void SetAllocatorFunctions(ImGuiMemAllocFunc alloc_func, ImGuiMemFreeFunc free_func, void* user_data = NULL); + IMGUI_API void GetAllocatorFunctions(ImGuiMemAllocFunc* p_alloc_func, ImGuiMemFreeFunc* p_free_func, void** p_user_data); IMGUI_API void* MemAlloc(size_t size); IMGUI_API void MemFree(void* ptr); @@ -874,7 +948,7 @@ enum ImGuiWindowFlags_ ImGuiWindowFlags_NoMove = 1 << 2, // Disable user moving the window ImGuiWindowFlags_NoScrollbar = 1 << 3, // Disable scrollbars (window can still scroll with mouse or programmatically) ImGuiWindowFlags_NoScrollWithMouse = 1 << 4, // Disable user vertically scrolling with mouse wheel. On child window, mouse wheel will be forwarded to the parent unless NoScrollbar is also set. - ImGuiWindowFlags_NoCollapse = 1 << 5, // Disable user collapsing window by double-clicking on it + ImGuiWindowFlags_NoCollapse = 1 << 5, // Disable user collapsing window by double-clicking on it. Also referred to as Window Menu Button (e.g. within a docking node). ImGuiWindowFlags_AlwaysAutoResize = 1 << 6, // Resize every window to its content every frame ImGuiWindowFlags_NoBackground = 1 << 7, // Disable drawing background color (WindowBg, etc.) and outside border. Similar as using SetNextWindowBgAlpha(0.0f). ImGuiWindowFlags_NoSavedSettings = 1 << 8, // Never load/save settings in .ini file @@ -888,21 +962,19 @@ enum ImGuiWindowFlags_ ImGuiWindowFlags_AlwaysUseWindowPadding = 1 << 16, // Ensure child windows without border uses style.WindowPadding (ignored by default for non-bordered child windows, because more convenient) ImGuiWindowFlags_NoNavInputs = 1 << 18, // No gamepad/keyboard navigation within the window ImGuiWindowFlags_NoNavFocus = 1 << 19, // No focusing toward this window with gamepad/keyboard navigation (e.g. skipped by CTRL+TAB) - ImGuiWindowFlags_UnsavedDocument = 1 << 20, // Append '*' to title without affecting the ID, as a convenience to avoid using the ### operator. When used in a tab/docking context, tab is selected on closure and closure is deferred by one frame to allow code to cancel the closure (with a confirmation popup, etc.) without flicker. + ImGuiWindowFlags_UnsavedDocument = 1 << 20, // Display a dot next to the title. When used in a tab/docking context, tab is selected when clicking the X + closure is not assumed (will wait for user to stop submitting the tab). Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar. ImGuiWindowFlags_NoNav = ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus, ImGuiWindowFlags_NoDecoration = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoCollapse, ImGuiWindowFlags_NoInputs = ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus, // [Internal] - ImGuiWindowFlags_NavFlattened = 1 << 23, // [BETA] Allow gamepad/keyboard navigation to cross over parent border to this child (only use on child that have no scrolling!) + ImGuiWindowFlags_NavFlattened = 1 << 23, // [BETA] On child window: allow gamepad/keyboard navigation to cross over parent border to this child or between sibling child windows. ImGuiWindowFlags_ChildWindow = 1 << 24, // Don't use! For internal use by BeginChild() ImGuiWindowFlags_Tooltip = 1 << 25, // Don't use! For internal use by BeginTooltip() ImGuiWindowFlags_Popup = 1 << 26, // Don't use! For internal use by BeginPopup() ImGuiWindowFlags_Modal = 1 << 27, // Don't use! For internal use by BeginPopupModal() ImGuiWindowFlags_ChildMenu = 1 << 28 // Don't use! For internal use by BeginMenu() - - // [Obsolete] - //ImGuiWindowFlags_ResizeFromAnySide = 1 << 17, // --> Set io.ConfigWindowsResizeFromEdges=true and make sure mouse cursors are supported by backend (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) + //ImGuiWindowFlags_ResizeFromAnySide = 1 << 17, // [Obsolete] --> Set io.ConfigWindowsResizeFromEdges=true and make sure mouse cursors are supported by backend (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) }; // Flags for ImGui::InputText() @@ -922,16 +994,18 @@ enum ImGuiInputTextFlags_ ImGuiInputTextFlags_AllowTabInput = 1 << 10, // Pressing TAB input a '\t' character into the text field ImGuiInputTextFlags_CtrlEnterForNewLine = 1 << 11, // In multi-line mode, unfocus with Enter, add new line with Ctrl+Enter (default is opposite: unfocus with Ctrl+Enter, add line with Enter). ImGuiInputTextFlags_NoHorizontalScroll = 1 << 12, // Disable following the cursor horizontally - ImGuiInputTextFlags_AlwaysInsertMode = 1 << 13, // Insert mode + ImGuiInputTextFlags_AlwaysOverwrite = 1 << 13, // Overwrite mode ImGuiInputTextFlags_ReadOnly = 1 << 14, // Read-only mode ImGuiInputTextFlags_Password = 1 << 15, // Password mode, display all characters as '*' ImGuiInputTextFlags_NoUndoRedo = 1 << 16, // Disable undo/redo. Note that input text owns the text data while active, if you want to provide your own undo/redo stack you need e.g. to call ClearActiveID(). ImGuiInputTextFlags_CharsScientific = 1 << 17, // Allow 0123456789.+-*/eE (Scientific notation input) ImGuiInputTextFlags_CallbackResize = 1 << 18, // Callback on buffer capacity changes request (beyond 'buf_size' parameter value), allowing the string to grow. Notify when the string wants to be resized (for string types which hold a cache of their Size). You will be provided a new BufSize in the callback and NEED to honor it. (see misc/cpp/imgui_stdlib.h for an example of using this) - ImGuiInputTextFlags_CallbackEdit = 1 << 19, // Callback on any edit (note that InputText() already returns true on edit, the callback is useful mainly to manipulate the underlying buffer while focus is active) - // [Internal] - ImGuiInputTextFlags_Multiline = 1 << 20, // For internal use by InputTextMultiline() - ImGuiInputTextFlags_NoMarkEdited = 1 << 21 // For internal use by functions using InputText() before reformatting data + ImGuiInputTextFlags_CallbackEdit = 1 << 19 // Callback on any edit (note that InputText() already returns true on edit, the callback is useful mainly to manipulate the underlying buffer while focus is active) + + // Obsolete names (will be removed soon) +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + , ImGuiInputTextFlags_AlwaysInsertMode = ImGuiInputTextFlags_AlwaysOverwrite // [renamed in 1.82] name was not matching behavior +#endif }; // Flags for ImGui::TreeNodeEx(), ImGui::CollapsingHeader*() @@ -1024,7 +1098,7 @@ enum ImGuiTabBarFlags_ enum ImGuiTabItemFlags_ { ImGuiTabItemFlags_None = 0, - ImGuiTabItemFlags_UnsavedDocument = 1 << 0, // Append '*' to title without affecting the ID, as a convenience to avoid using the ### operator. Also: tab is selected on closure and closure is deferred by one frame to allow code to undo it without flicker. + ImGuiTabItemFlags_UnsavedDocument = 1 << 0, // Display a dot next to the title + tab is selected when clicking the X + closure is not assumed (will wait for user to stop submitting the tab). Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar. ImGuiTabItemFlags_SetSelected = 1 << 1, // Trigger flag to programmatically make the tab selected when calling BeginTabItem() ImGuiTabItemFlags_NoCloseWithMiddleMouseButton = 1 << 2, // Disable behavior of closing tabs (that are submitted with p_open != NULL) with middle mouse button. You can still repro this behavior on user's side with if (IsItemHovered() && IsMouseClicked(2)) *p_open = false. ImGuiTabItemFlags_NoPushId = 1 << 3, // Don't call PushID(tab->ID)/PopID() on BeginTabItem()/EndTabItem() @@ -1035,8 +1109,7 @@ enum ImGuiTabItemFlags_ }; // Flags for ImGui::BeginTable() -// [BETA API] API may evolve slightly! If you use this, please update to the next version when it comes out! -// - Important! Sizing policies have complex and subtle side effects, more so than you would expect. +// - Important! Sizing policies have complex and subtle side effects, much more so than you would expect. // Read comments/demos carefully + experiment with live demos to get acquainted with them. // - The DEFAULT sizing policies are: // - Default to ImGuiTableFlags_SizingFixedFit if ScrollX is on, or if host window has ImGuiWindowFlags_AlwaysAutoResize. @@ -1044,8 +1117,8 @@ enum ImGuiTabItemFlags_ // - When ScrollX is off: // - Table defaults to ImGuiTableFlags_SizingStretchSame -> all Columns defaults to ImGuiTableColumnFlags_WidthStretch with same weight. // - Columns sizing policy allowed: Stretch (default), Fixed/Auto. -// - Fixed Columns will generally obtain their requested width (unless the table cannot fit them all). -// - Stretch Columns will share the remaining width. +// - Fixed Columns (if any) will generally obtain their requested width (unless the table cannot fit them all). +// - Stretch Columns will share the remaining width according to their respective weight. // - Mixed Fixed/Stretch columns is possible but has various side-effects on resizing behaviors. // The typical use of mixing sizing policies is: any number of LEADING Fixed columns, followed by one or two TRAILING Stretch columns. // (this is because the visible order of columns have subtle but necessary effects on how they react to manual resizing). @@ -1084,7 +1157,7 @@ enum ImGuiTableFlags_ ImGuiTableFlags_SizingFixedFit = 1 << 13, // Columns default to _WidthFixed or _WidthAuto (if resizable or not resizable), matching contents width. ImGuiTableFlags_SizingFixedSame = 2 << 13, // Columns default to _WidthFixed or _WidthAuto (if resizable or not resizable), matching the maximum contents width of all columns. Implicitly enable ImGuiTableFlags_NoKeepColumnsVisible. ImGuiTableFlags_SizingStretchProp = 3 << 13, // Columns default to _WidthStretch with default weights proportional to each columns contents widths. - ImGuiTableFlags_SizingStretchSame = 4 << 13, // Columns default to _WidthStretch with default weights all equal, unless overriden by TableSetupColumn(). + ImGuiTableFlags_SizingStretchSame = 4 << 13, // Columns default to _WidthStretch with default weights all equal, unless overridden by TableSetupColumn(). // Sizing Extra Options ImGuiTableFlags_NoHostExtendX = 1 << 16, // Make outer width auto-fit to columns, overriding outer_size.x value. Only available when ScrollX/ScrollY are disabled and Stretch columns are not used. ImGuiTableFlags_NoHostExtendY = 1 << 17, // Make outer height stop exactly at outer_size.y (prevent auto-extending table past the limit). Only available when ScrollX/ScrollY are disabled. Data below the limit will be clipped and not visible. @@ -1118,28 +1191,30 @@ enum ImGuiTableColumnFlags_ { // Input configuration flags ImGuiTableColumnFlags_None = 0, - ImGuiTableColumnFlags_DefaultHide = 1 << 0, // Default as a hidden/disabled column. - ImGuiTableColumnFlags_DefaultSort = 1 << 1, // Default as a sorting column. - ImGuiTableColumnFlags_WidthStretch = 1 << 2, // Column will stretch. Preferable with horizontal scrolling disabled (default if table sizing policy is _SizingStretchSame or _SizingStretchProp). - ImGuiTableColumnFlags_WidthFixed = 1 << 3, // Column will not stretch. Preferable with horizontal scrolling enabled (default if table sizing policy is _SizingFixedFit and table is resizable). - ImGuiTableColumnFlags_NoResize = 1 << 4, // Disable manual resizing. - ImGuiTableColumnFlags_NoReorder = 1 << 5, // Disable manual reordering this column, this will also prevent other columns from crossing over this column. - ImGuiTableColumnFlags_NoHide = 1 << 6, // Disable ability to hide/disable this column. - ImGuiTableColumnFlags_NoClip = 1 << 7, // Disable clipping for this column (all NoClip columns will render in a same draw command). - ImGuiTableColumnFlags_NoSort = 1 << 8, // Disable ability to sort on this field (even if ImGuiTableFlags_Sortable is set on the table). - ImGuiTableColumnFlags_NoSortAscending = 1 << 9, // Disable ability to sort in the ascending direction. - ImGuiTableColumnFlags_NoSortDescending = 1 << 10, // Disable ability to sort in the descending direction. - ImGuiTableColumnFlags_NoHeaderWidth = 1 << 11, // Disable header text width contribution to automatic column width. - ImGuiTableColumnFlags_PreferSortAscending = 1 << 12, // Make the initial sort direction Ascending when first sorting on this column (default). - ImGuiTableColumnFlags_PreferSortDescending = 1 << 13, // Make the initial sort direction Descending when first sorting on this column. - ImGuiTableColumnFlags_IndentEnable = 1 << 14, // Use current Indent value when entering cell (default for column 0). - ImGuiTableColumnFlags_IndentDisable = 1 << 15, // Ignore current Indent value when entering cell (default for columns > 0). Indentation changes _within_ the cell will still be honored. + ImGuiTableColumnFlags_Disabled = 1 << 0, // Overriding/master disable flag: hide column, won't show in context menu (unlike calling TableSetColumnEnabled() which manipulates the user accessible state) + ImGuiTableColumnFlags_DefaultHide = 1 << 1, // Default as a hidden/disabled column. + ImGuiTableColumnFlags_DefaultSort = 1 << 2, // Default as a sorting column. + ImGuiTableColumnFlags_WidthStretch = 1 << 3, // Column will stretch. Preferable with horizontal scrolling disabled (default if table sizing policy is _SizingStretchSame or _SizingStretchProp). + ImGuiTableColumnFlags_WidthFixed = 1 << 4, // Column will not stretch. Preferable with horizontal scrolling enabled (default if table sizing policy is _SizingFixedFit and table is resizable). + ImGuiTableColumnFlags_NoResize = 1 << 5, // Disable manual resizing. + ImGuiTableColumnFlags_NoReorder = 1 << 6, // Disable manual reordering this column, this will also prevent other columns from crossing over this column. + ImGuiTableColumnFlags_NoHide = 1 << 7, // Disable ability to hide/disable this column. + ImGuiTableColumnFlags_NoClip = 1 << 8, // Disable clipping for this column (all NoClip columns will render in a same draw command). + ImGuiTableColumnFlags_NoSort = 1 << 9, // Disable ability to sort on this field (even if ImGuiTableFlags_Sortable is set on the table). + ImGuiTableColumnFlags_NoSortAscending = 1 << 10, // Disable ability to sort in the ascending direction. + ImGuiTableColumnFlags_NoSortDescending = 1 << 11, // Disable ability to sort in the descending direction. + ImGuiTableColumnFlags_NoHeaderLabel = 1 << 12, // TableHeadersRow() will not submit label for this column. Convenient for some small columns. Name will still appear in context menu. + ImGuiTableColumnFlags_NoHeaderWidth = 1 << 13, // Disable header text width contribution to automatic column width. + ImGuiTableColumnFlags_PreferSortAscending = 1 << 14, // Make the initial sort direction Ascending when first sorting on this column (default). + ImGuiTableColumnFlags_PreferSortDescending = 1 << 15, // Make the initial sort direction Descending when first sorting on this column. + ImGuiTableColumnFlags_IndentEnable = 1 << 16, // Use current Indent value when entering cell (default for column 0). + ImGuiTableColumnFlags_IndentDisable = 1 << 17, // Ignore current Indent value when entering cell (default for columns > 0). Indentation changes _within_ the cell will still be honored. // Output status flags, read-only via TableGetColumnFlags() - ImGuiTableColumnFlags_IsEnabled = 1 << 20, // Status: is enabled == not hidden by user/api (referred to as "Hide" in _DefaultHide and _NoHide) flags. - ImGuiTableColumnFlags_IsVisible = 1 << 21, // Status: is visible == is enabled AND not clipped by scrolling. - ImGuiTableColumnFlags_IsSorted = 1 << 22, // Status: is currently part of the sort specs - ImGuiTableColumnFlags_IsHovered = 1 << 23, // Status: is hovered by mouse + ImGuiTableColumnFlags_IsEnabled = 1 << 24, // Status: is enabled == not hidden by user/api (referred to as "Hide" in _DefaultHide and _NoHide) flags. + ImGuiTableColumnFlags_IsVisible = 1 << 25, // Status: is visible == is enabled AND not clipped by scrolling. + ImGuiTableColumnFlags_IsSorted = 1 << 26, // Status: is currently part of the sort specs + ImGuiTableColumnFlags_IsHovered = 1 << 27, // Status: is hovered by mouse // [Internal] Combinations and masks ImGuiTableColumnFlags_WidthMask_ = ImGuiTableColumnFlags_WidthStretch | ImGuiTableColumnFlags_WidthFixed, @@ -1157,7 +1232,7 @@ enum ImGuiTableColumnFlags_ enum ImGuiTableRowFlags_ { ImGuiTableRowFlags_None = 0, - ImGuiTableRowFlags_Headers = 1 << 0 // Identify header row (set default background color + width of its contents accounted different for auto column width) + ImGuiTableRowFlags_Headers = 1 << 0 // Identify header row (set default background color + width of its contents accounted differently for auto column width) }; // Enum for ImGui::TableSetBgColor() @@ -1181,9 +1256,11 @@ enum ImGuiTableBgTarget_ enum ImGuiFocusedFlags_ { ImGuiFocusedFlags_None = 0, - ImGuiFocusedFlags_ChildWindows = 1 << 0, // IsWindowFocused(): Return true if any children of the window is focused - ImGuiFocusedFlags_RootWindow = 1 << 1, // IsWindowFocused(): Test from root window (top most parent of the current hierarchy) - ImGuiFocusedFlags_AnyWindow = 1 << 2, // IsWindowFocused(): Return true if any window is focused. Important: If you are trying to tell how to dispatch your low-level inputs, do NOT use this. Use 'io.WantCaptureMouse' instead! Please read the FAQ! + ImGuiFocusedFlags_ChildWindows = 1 << 0, // Return true if any children of the window is focused + ImGuiFocusedFlags_RootWindow = 1 << 1, // Test from root window (top most parent of the current hierarchy) + ImGuiFocusedFlags_AnyWindow = 1 << 2, // Return true if any window is focused. Important: If you are trying to tell how to dispatch your low-level inputs, do NOT use this. Use 'io.WantCaptureMouse' instead! Please read the FAQ! + ImGuiFocusedFlags_NoPopupHierarchy = 1 << 3, // Do not consider popup hierarchy (do not treat popup emitter as parent of popup) (when used with _ChildWindows or _RootWindow) + //ImGuiFocusedFlags_DockHierarchy = 1 << 4, // Consider docking hierarchy (treat dockspace host as parent of docked window) (when used with _ChildWindows or _RootWindow) ImGuiFocusedFlags_RootAndChildWindows = ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows }; @@ -1196,11 +1273,13 @@ enum ImGuiHoveredFlags_ ImGuiHoveredFlags_ChildWindows = 1 << 0, // IsWindowHovered() only: Return true if any children of the window is hovered ImGuiHoveredFlags_RootWindow = 1 << 1, // IsWindowHovered() only: Test from root window (top most parent of the current hierarchy) ImGuiHoveredFlags_AnyWindow = 1 << 2, // IsWindowHovered() only: Return true if any window is hovered - ImGuiHoveredFlags_AllowWhenBlockedByPopup = 1 << 3, // Return true even if a popup window is normally blocking access to this item/window - //ImGuiHoveredFlags_AllowWhenBlockedByModal = 1 << 4, // Return true even if a modal popup window is normally blocking access to this item/window. FIXME-TODO: Unavailable yet. - ImGuiHoveredFlags_AllowWhenBlockedByActiveItem = 1 << 5, // Return true even if an active item is blocking access to this item/window. Useful for Drag and Drop patterns. - ImGuiHoveredFlags_AllowWhenOverlapped = 1 << 6, // Return true even if the position is obstructed or overlapped by another window - ImGuiHoveredFlags_AllowWhenDisabled = 1 << 7, // Return true even if the item is disabled + ImGuiHoveredFlags_NoPopupHierarchy = 1 << 3, // IsWindowHovered() only: Do not consider popup hierarchy (do not treat popup emitter as parent of popup) (when used with _ChildWindows or _RootWindow) + //ImGuiHoveredFlags_DockHierarchy = 1 << 4, // IsWindowHovered() only: Consider docking hierarchy (treat dockspace host as parent of docked window) (when used with _ChildWindows or _RootWindow) + ImGuiHoveredFlags_AllowWhenBlockedByPopup = 1 << 5, // Return true even if a popup window is normally blocking access to this item/window + //ImGuiHoveredFlags_AllowWhenBlockedByModal = 1 << 6, // Return true even if a modal popup window is normally blocking access to this item/window. FIXME-TODO: Unavailable yet. + ImGuiHoveredFlags_AllowWhenBlockedByActiveItem = 1 << 7, // Return true even if an active item is blocking access to this item/window. Useful for Drag and Drop patterns. + ImGuiHoveredFlags_AllowWhenOverlapped = 1 << 8, // IsItemHovered() only: Return true even if the position is obstructed or overlapped by another window + ImGuiHoveredFlags_AllowWhenDisabled = 1 << 9, // IsItemHovered() only: Return true even if the item is disabled ImGuiHoveredFlags_RectOnly = ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenOverlapped, ImGuiHoveredFlags_RootAndChildWindows = ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows }; @@ -1262,10 +1341,11 @@ enum ImGuiSortDirection_ ImGuiSortDirection_Descending = 2 // Descending = 9->0, Z->A etc. }; -// User fill ImGuiIO.KeyMap[] array with indices into the ImGuiIO.KeysDown[512] array enum ImGuiKey_ { - ImGuiKey_Tab, + // Keyboard + ImGuiKey_None = 0, + ImGuiKey_Tab = 512, // == ImGuiKey_NamedKey_BEGIN ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_UpArrow, @@ -1280,66 +1360,148 @@ enum ImGuiKey_ ImGuiKey_Space, ImGuiKey_Enter, ImGuiKey_Escape, - ImGuiKey_KeyPadEnter, - ImGuiKey_A, // for text edit CTRL+A: select all - ImGuiKey_C, // for text edit CTRL+C: copy - ImGuiKey_V, // for text edit CTRL+V: paste - ImGuiKey_X, // for text edit CTRL+X: cut - ImGuiKey_Y, // for text edit CTRL+Y: redo - ImGuiKey_Z, // for text edit CTRL+Z: undo - ImGuiKey_COUNT + ImGuiKey_LeftCtrl, ImGuiKey_LeftShift, ImGuiKey_LeftAlt, ImGuiKey_LeftSuper, + ImGuiKey_RightCtrl, ImGuiKey_RightShift, ImGuiKey_RightAlt, ImGuiKey_RightSuper, + ImGuiKey_Menu, + ImGuiKey_0, ImGuiKey_1, ImGuiKey_2, ImGuiKey_3, ImGuiKey_4, ImGuiKey_5, ImGuiKey_6, ImGuiKey_7, ImGuiKey_8, ImGuiKey_9, + ImGuiKey_A, ImGuiKey_B, ImGuiKey_C, ImGuiKey_D, ImGuiKey_E, ImGuiKey_F, ImGuiKey_G, ImGuiKey_H, ImGuiKey_I, ImGuiKey_J, + ImGuiKey_K, ImGuiKey_L, ImGuiKey_M, ImGuiKey_N, ImGuiKey_O, ImGuiKey_P, ImGuiKey_Q, ImGuiKey_R, ImGuiKey_S, ImGuiKey_T, + ImGuiKey_U, ImGuiKey_V, ImGuiKey_W, ImGuiKey_X, ImGuiKey_Y, ImGuiKey_Z, + ImGuiKey_F1, ImGuiKey_F2, ImGuiKey_F3, ImGuiKey_F4, ImGuiKey_F5, ImGuiKey_F6, + ImGuiKey_F7, ImGuiKey_F8, ImGuiKey_F9, ImGuiKey_F10, ImGuiKey_F11, ImGuiKey_F12, + ImGuiKey_Apostrophe, // ' + ImGuiKey_Comma, // , + ImGuiKey_Minus, // - + ImGuiKey_Period, // . + ImGuiKey_Slash, // / + ImGuiKey_Semicolon, // ; + ImGuiKey_Equal, // = + ImGuiKey_LeftBracket, // [ + ImGuiKey_Backslash, // \ (this text inhibit multiline comment caused by backslash) + ImGuiKey_RightBracket, // ] + ImGuiKey_GraveAccent, // ` + ImGuiKey_CapsLock, + ImGuiKey_ScrollLock, + ImGuiKey_NumLock, + ImGuiKey_PrintScreen, + ImGuiKey_Pause, + ImGuiKey_Keypad0, ImGuiKey_Keypad1, ImGuiKey_Keypad2, ImGuiKey_Keypad3, ImGuiKey_Keypad4, + ImGuiKey_Keypad5, ImGuiKey_Keypad6, ImGuiKey_Keypad7, ImGuiKey_Keypad8, ImGuiKey_Keypad9, + ImGuiKey_KeypadDecimal, + ImGuiKey_KeypadDivide, + ImGuiKey_KeypadMultiply, + ImGuiKey_KeypadSubtract, + ImGuiKey_KeypadAdd, + ImGuiKey_KeypadEnter, + ImGuiKey_KeypadEqual, + + // Gamepad (some of those are analog values, 0.0f to 1.0f) // NAVIGATION action + ImGuiKey_GamepadStart, // Menu (Xbox) + (Switch) Start/Options (PS) // -- + ImGuiKey_GamepadBack, // View (Xbox) - (Switch) Share (PS) // -- + ImGuiKey_GamepadFaceUp, // Y (Xbox) X (Switch) Triangle (PS) // -> ImGuiNavInput_Input + ImGuiKey_GamepadFaceDown, // A (Xbox) B (Switch) Cross (PS) // -> ImGuiNavInput_Activate + ImGuiKey_GamepadFaceLeft, // X (Xbox) Y (Switch) Square (PS) // -> ImGuiNavInput_Menu + ImGuiKey_GamepadFaceRight, // B (Xbox) A (Switch) Circle (PS) // -> ImGuiNavInput_Cancel + ImGuiKey_GamepadDpadUp, // D-pad Up // -> ImGuiNavInput_DpadUp + ImGuiKey_GamepadDpadDown, // D-pad Down // -> ImGuiNavInput_DpadDown + ImGuiKey_GamepadDpadLeft, // D-pad Left // -> ImGuiNavInput_DpadLeft + ImGuiKey_GamepadDpadRight, // D-pad Right // -> ImGuiNavInput_DpadRight + ImGuiKey_GamepadL1, // L Bumper (Xbox) L (Switch) L1 (PS) // -> ImGuiNavInput_FocusPrev + ImGuiNavInput_TweakSlow + ImGuiKey_GamepadR1, // R Bumper (Xbox) R (Switch) R1 (PS) // -> ImGuiNavInput_FocusNext + ImGuiNavInput_TweakFast + ImGuiKey_GamepadL2, // L Trigger (Xbox) ZL (Switch) L2 (PS) [Analog] + ImGuiKey_GamepadR2, // R Trigger (Xbox) ZR (Switch) R2 (PS) [Analog] + ImGuiKey_GamepadL3, // L Thumbstick (Xbox) L3 (Switch) L3 (PS) + ImGuiKey_GamepadR3, // R Thumbstick (Xbox) R3 (Switch) R3 (PS) + ImGuiKey_GamepadLStickUp, // [Analog] // -> ImGuiNavInput_LStickUp + ImGuiKey_GamepadLStickDown, // [Analog] // -> ImGuiNavInput_LStickDown + ImGuiKey_GamepadLStickLeft, // [Analog] // -> ImGuiNavInput_LStickLeft + ImGuiKey_GamepadLStickRight, // [Analog] // -> ImGuiNavInput_LStickRight + ImGuiKey_GamepadRStickUp, // [Analog] + ImGuiKey_GamepadRStickDown, // [Analog] + ImGuiKey_GamepadRStickLeft, // [Analog] + ImGuiKey_GamepadRStickRight, // [Analog] + + // Keyboard Modifiers + // - This is mirroring the data also written to io.KeyCtrl, io.KeyShift, io.KeyAlt, io.KeySuper, in a format allowing + // them to be accessed via standard key API, allowing calls such as IsKeyPressed(), IsKeyReleased(), querying duration etc. + // - Code polling every keys (e.g. an interface to detect a key press for input mapping) might want to ignore those + // and prefer using the real keys (e.g. ImGuiKey_LeftCtrl, ImGuiKey_RightCtrl instead of ImGuiKey_ModCtrl). + // - In theory the value of keyboard modifiers should be roughly equivalent to a logical or of the equivalent left/right keys. + // In practice: it's complicated; mods are often provided from different sources. Keyboard layout, IME, sticky keys and + // backends tend to interfere and break that equivalence. The safer decision is to relay that ambiguity down to the end-user... + ImGuiKey_ModCtrl, + ImGuiKey_ModShift, + ImGuiKey_ModAlt, + ImGuiKey_ModSuper, + + ImGuiKey_COUNT, // No valid ImGuiKey is ever greater than this value + + // [Internal] Prior to 1.87 we required user to fill io.KeysDown[512] using their own native index + a io.KeyMap[] array. + // We are ditching this method but keeping a legacy path for user code doing e.g. IsKeyPressed(MY_NATIVE_KEY_CODE) + ImGuiKey_NamedKey_BEGIN = 512, + ImGuiKey_NamedKey_END = ImGuiKey_COUNT, + ImGuiKey_NamedKey_COUNT = ImGuiKey_NamedKey_END - ImGuiKey_NamedKey_BEGIN, +#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO + ImGuiKey_KeysData_SIZE = ImGuiKey_NamedKey_COUNT, // Size of KeysData[]: only hold named keys + ImGuiKey_KeysData_OFFSET = ImGuiKey_NamedKey_BEGIN // First key stored in KeysData[0] +#else + ImGuiKey_KeysData_SIZE = ImGuiKey_COUNT, // Size of KeysData[]: hold legacy 0..512 keycodes + named keys + ImGuiKey_KeysData_OFFSET = 0 // First key stored in KeysData[0] +#endif + +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + , ImGuiKey_KeyPadEnter = ImGuiKey_KeypadEnter // Renamed in 1.87 +#endif }; -// To test io.KeyMods (which is a combination of individual fields io.KeyCtrl, io.KeyShift, io.KeyAlt set by user/backend) +// Helper "flags" version of key-mods to store and compare multiple key-mods easily. Sometimes used for storage (e.g. io.KeyMods) but otherwise not much used in public API. enum ImGuiKeyModFlags_ { - ImGuiKeyModFlags_None = 0, - ImGuiKeyModFlags_Ctrl = 1 << 0, - ImGuiKeyModFlags_Shift = 1 << 1, - ImGuiKeyModFlags_Alt = 1 << 2, - ImGuiKeyModFlags_Super = 1 << 3 + ImGuiKeyModFlags_None = 0, + ImGuiKeyModFlags_Ctrl = 1 << 0, + ImGuiKeyModFlags_Shift = 1 << 1, + ImGuiKeyModFlags_Alt = 1 << 2, + ImGuiKeyModFlags_Super = 1 << 3 // Cmd/Super/Windows key }; // Gamepad/Keyboard navigation -// Keyboard: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable. NewFrame() will automatically fill io.NavInputs[] based on your io.KeysDown[] + io.KeyMap[] arrays. +// Keyboard: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable. NewFrame() will automatically fill io.NavInputs[] based on your io.AddKeyEvent() calls. // Gamepad: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable. Backend: set ImGuiBackendFlags_HasGamepad and fill the io.NavInputs[] fields before calling NewFrame(). Note that io.NavInputs[] is cleared by EndFrame(). // Read instructions in imgui.cpp for more details. Download PNG/PSD at http://dearimgui.org/controls_sheets. enum ImGuiNavInput_ { // Gamepad Mapping - ImGuiNavInput_Activate, // activate / open / toggle / tweak value // e.g. Cross (PS4), A (Xbox), A (Switch), Space (Keyboard) - ImGuiNavInput_Cancel, // cancel / close / exit // e.g. Circle (PS4), B (Xbox), B (Switch), Escape (Keyboard) - ImGuiNavInput_Input, // text input / on-screen keyboard // e.g. Triang.(PS4), Y (Xbox), X (Switch), Return (Keyboard) - ImGuiNavInput_Menu, // tap: toggle menu / hold: focus, move, resize // e.g. Square (PS4), X (Xbox), Y (Switch), Alt (Keyboard) - ImGuiNavInput_DpadLeft, // move / tweak / resize window (w/ PadMenu) // e.g. D-pad Left/Right/Up/Down (Gamepads), Arrow keys (Keyboard) + ImGuiNavInput_Activate, // Activate / Open / Toggle / Tweak value // e.g. Cross (PS4), A (Xbox), A (Switch), Space (Keyboard) + ImGuiNavInput_Cancel, // Cancel / Close / Exit // e.g. Circle (PS4), B (Xbox), B (Switch), Escape (Keyboard) + ImGuiNavInput_Input, // Text input / On-Screen keyboard // e.g. Triang.(PS4), Y (Xbox), X (Switch), Return (Keyboard) + ImGuiNavInput_Menu, // Tap: Toggle menu / Hold: Focus, Move, Resize // e.g. Square (PS4), X (Xbox), Y (Switch), Alt (Keyboard) + ImGuiNavInput_DpadLeft, // Move / Tweak / Resize window (w/ PadMenu) // e.g. D-pad Left/Right/Up/Down (Gamepads), Arrow keys (Keyboard) ImGuiNavInput_DpadRight, // ImGuiNavInput_DpadUp, // ImGuiNavInput_DpadDown, // - ImGuiNavInput_LStickLeft, // scroll / move window (w/ PadMenu) // e.g. Left Analog Stick Left/Right/Up/Down + ImGuiNavInput_LStickLeft, // Scroll / Move window (w/ PadMenu) // e.g. Left Analog Stick Left/Right/Up/Down ImGuiNavInput_LStickRight, // ImGuiNavInput_LStickUp, // ImGuiNavInput_LStickDown, // - ImGuiNavInput_FocusPrev, // next window (w/ PadMenu) // e.g. L1 or L2 (PS4), LB or LT (Xbox), L or ZL (Switch) - ImGuiNavInput_FocusNext, // prev window (w/ PadMenu) // e.g. R1 or R2 (PS4), RB or RT (Xbox), R or ZL (Switch) - ImGuiNavInput_TweakSlow, // slower tweaks // e.g. L1 or L2 (PS4), LB or LT (Xbox), L or ZL (Switch) - ImGuiNavInput_TweakFast, // faster tweaks // e.g. R1 or R2 (PS4), RB or RT (Xbox), R or ZL (Switch) + ImGuiNavInput_FocusPrev, // Focus Next window (w/ PadMenu) // e.g. L1 or L2 (PS4), LB or LT (Xbox), L or ZL (Switch) + ImGuiNavInput_FocusNext, // Focus Prev window (w/ PadMenu) // e.g. R1 or R2 (PS4), RB or RT (Xbox), R or ZL (Switch) + ImGuiNavInput_TweakSlow, // Slower tweaks // e.g. L1 or L2 (PS4), LB or LT (Xbox), L or ZL (Switch) + ImGuiNavInput_TweakFast, // Faster tweaks // e.g. R1 or R2 (PS4), RB or RT (Xbox), R or ZL (Switch) // [Internal] Don't use directly! This is used internally to differentiate keyboard from gamepad inputs for behaviors that require to differentiate them. - // Keyboard behavior that have no corresponding gamepad mapping (e.g. CTRL+TAB) will be directly reading from io.KeysDown[] instead of io.NavInputs[]. - ImGuiNavInput_KeyMenu_, // toggle menu // = io.KeyAlt - ImGuiNavInput_KeyLeft_, // move left // = Arrow keys - ImGuiNavInput_KeyRight_, // move right - ImGuiNavInput_KeyUp_, // move up - ImGuiNavInput_KeyDown_, // move down - ImGuiNavInput_COUNT, - ImGuiNavInput_InternalStart_ = ImGuiNavInput_KeyMenu_ + // Keyboard behavior that have no corresponding gamepad mapping (e.g. CTRL+TAB) will be directly reading from keyboard keys instead of io.NavInputs[]. + ImGuiNavInput_KeyLeft_, // Move left // = Arrow keys + ImGuiNavInput_KeyRight_, // Move right + ImGuiNavInput_KeyUp_, // Move up + ImGuiNavInput_KeyDown_, // Move down + ImGuiNavInput_COUNT }; // Configuration flags stored in io.ConfigFlags. Set by user/application. enum ImGuiConfigFlags_ { ImGuiConfigFlags_None = 0, - ImGuiConfigFlags_NavEnableKeyboard = 1 << 0, // Master keyboard navigation enable flag. NewFrame() will automatically fill io.NavInputs[] based on io.KeysDown[]. + ImGuiConfigFlags_NavEnableKeyboard = 1 << 0, // Master keyboard navigation enable flag. NewFrame() will automatically fill io.NavInputs[] based on io.AddKeyEvent() calls ImGuiConfigFlags_NavEnableGamepad = 1 << 1, // Master gamepad navigation enable flag. This is mostly to instruct your imgui backend to fill io.NavInputs[]. Backend also needs to set ImGuiBackendFlags_HasGamepad. ImGuiConfigFlags_NavEnableSetMousePos = 1 << 2, // Instruct navigation to move the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is awkward. Will update io.MousePos and set io.WantSetMousePos=true. If enabled you MUST honor io.WantSetMousePos requests in your backend, otherwise ImGui will react as if the mouse is jumping around back and forth. ImGuiConfigFlags_NavNoCaptureKeyboard = 1 << 3, // Instruct navigation to not set the io.WantCaptureKeyboard flag when io.NavActive is set. @@ -1431,6 +1593,7 @@ enum ImGuiStyleVar_ { // Enum name --------------------- // Member in ImGuiStyle structure (see ImGuiStyle for descriptions) ImGuiStyleVar_Alpha, // float Alpha + ImGuiStyleVar_DisabledAlpha, // float DisabledAlpha ImGuiStyleVar_WindowPadding, // ImVec2 WindowPadding ImGuiStyleVar_WindowRounding, // float WindowRounding ImGuiStyleVar_WindowBorderSize, // float WindowBorderSize @@ -1502,18 +1665,16 @@ enum ImGuiColorEditFlags_ // Defaults Options. You can set application defaults using SetColorEditOptions(). The intent is that you probably don't want to // override them in most of your calls. Let the user choose via the option menu and/or call SetColorEditOptions() once during startup. - ImGuiColorEditFlags__OptionsDefault = ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_InputRGB | ImGuiColorEditFlags_PickerHueBar, + ImGuiColorEditFlags_DefaultOptions_ = ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_InputRGB | ImGuiColorEditFlags_PickerHueBar, // [Internal] Masks - ImGuiColorEditFlags__DisplayMask = ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_DisplayHex, - ImGuiColorEditFlags__DataTypeMask = ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_Float, - ImGuiColorEditFlags__PickerMask = ImGuiColorEditFlags_PickerHueWheel | ImGuiColorEditFlags_PickerHueBar, - ImGuiColorEditFlags__InputMask = ImGuiColorEditFlags_InputRGB | ImGuiColorEditFlags_InputHSV + ImGuiColorEditFlags_DisplayMask_ = ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_DisplayHex, + ImGuiColorEditFlags_DataTypeMask_ = ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_Float, + ImGuiColorEditFlags_PickerMask_ = ImGuiColorEditFlags_PickerHueWheel | ImGuiColorEditFlags_PickerHueBar, + ImGuiColorEditFlags_InputMask_ = ImGuiColorEditFlags_InputRGB | ImGuiColorEditFlags_InputHSV // Obsolete names (will be removed) -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - , ImGuiColorEditFlags_RGB = ImGuiColorEditFlags_DisplayRGB, ImGuiColorEditFlags_HSV = ImGuiColorEditFlags_DisplayHSV, ImGuiColorEditFlags_HEX = ImGuiColorEditFlags_DisplayHex // [renamed in 1.69] -#endif + // ImGuiColorEditFlags_RGB = ImGuiColorEditFlags_DisplayRGB, ImGuiColorEditFlags_HSV = ImGuiColorEditFlags_DisplayHSV, ImGuiColorEditFlags_HEX = ImGuiColorEditFlags_DisplayHex // [renamed in 1.69] }; // Flags for DragFloat(), DragInt(), SliderFloat(), SliderInt() etc. @@ -1602,6 +1763,7 @@ template void IM_DELETE(T* p) { if (p) { p->~T(); ImGui::MemFree(p // Do NOT use this class as a std::vector replacement in your own code! Many of the structures used by dear imgui can be safely initialized by a zero-memset. //----------------------------------------------------------------------------- +IM_MSVC_RUNTIME_CHECKS_OFF template struct ImVector { @@ -1618,7 +1780,11 @@ struct ImVector inline ImVector() { Size = Capacity = 0; Data = NULL; } inline ImVector(const ImVector& src) { Size = Capacity = 0; Data = NULL; operator=(src); } inline ImVector& operator=(const ImVector& src) { clear(); resize(src.Size); memcpy(Data, src.Data, (size_t)Size * sizeof(T)); return *this; } - inline ~ImVector() { if (Data) IM_FREE(Data); } + inline ~ImVector() { if (Data) IM_FREE(Data); } // Important: does not destruct anything + + inline void clear() { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } } // Important: does not destruct anything + inline void clear_delete() { for (int n = 0; n < Size; n++) IM_DELETE(Data[n]); clear(); } // Important: never called automatically! always explicit. + inline void clear_destruct() { for (int n = 0; n < Size; n++) Data[n].~T(); clear(); } // Important: never called automatically! always explicit. inline bool empty() const { return Size == 0; } inline int size() const { return Size; } @@ -1628,7 +1794,6 @@ struct ImVector inline T& operator[](int i) { IM_ASSERT(i >= 0 && i < Size); return Data[i]; } inline const T& operator[](int i) const { IM_ASSERT(i >= 0 && i < Size); return Data[i]; } - inline void clear() { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } } inline T* begin() { return Data; } inline const T* begin() const { return Data; } inline T* end() { return Data + Size; } @@ -1650,7 +1815,7 @@ struct ImVector inline void pop_back() { IM_ASSERT(Size > 0); Size--; } inline void push_front(const T& v) { if (Size == 0) push_back(v); else insert(Data, v); } inline T* erase(const T* it) { IM_ASSERT(it >= Data && it < Data + Size); const ptrdiff_t off = it - Data; memmove(Data + off, Data + off + 1, ((size_t)Size - (size_t)off - 1) * sizeof(T)); Size--; return Data + off; } - inline T* erase(const T* it, const T* it_last){ IM_ASSERT(it >= Data && it < Data + Size && it_last > it && it_last <= Data + Size); const ptrdiff_t count = it_last - it; const ptrdiff_t off = it - Data; memmove(Data + off, Data + off + count, ((size_t)Size - (size_t)off - count) * sizeof(T)); Size -= (int)count; return Data + off; } + inline T* erase(const T* it, const T* it_last){ IM_ASSERT(it >= Data && it < Data + Size && it_last > it && it_last <= Data + Size); const ptrdiff_t count = it_last - it; const ptrdiff_t off = it - Data; memmove(Data + off, Data + off + count, ((size_t)Size - (size_t)off - (size_t)count) * sizeof(T)); Size -= (int)count; return Data + off; } inline T* erase_unsorted(const T* it) { IM_ASSERT(it >= Data && it < Data + Size); const ptrdiff_t off = it - Data; if (it < Data + Size - 1) memcpy(Data + off, Data + Size - 1, sizeof(T)); Size--; return Data + off; } inline T* insert(const T* it, const T& v) { IM_ASSERT(it >= Data && it <= Data + Size); const ptrdiff_t off = it - Data; if (Size == Capacity) reserve(_grow_capacity(Size + 1)); if (off < (int)Size) memmove(Data + off + 1, Data + off, ((size_t)Size - (size_t)off) * sizeof(T)); memcpy(&Data[off], &v, sizeof(v)); Size++; return Data + off; } inline bool contains(const T& v) const { const T* data = Data; const T* data_end = Data + Size; while (data < data_end) if (*data++ == v) return true; return false; } @@ -1660,6 +1825,7 @@ struct ImVector inline bool find_erase_unsorted(const T& v) { const T* it = find(v); if (it < Data + Size) { erase_unsorted(it); return true; } return false; } inline int index_from_ptr(const T* it) const { IM_ASSERT(it >= Data && it < Data + Size); const ptrdiff_t off = it - Data; return (int)off; } }; +IM_MSVC_RUNTIME_CHECKS_RESTORE //----------------------------------------------------------------------------- // [SECTION] ImGuiStyle @@ -1672,6 +1838,7 @@ struct ImVector struct ImGuiStyle { float Alpha; // Global alpha applies to everything in Dear ImGui. + float DisabledAlpha; // Additional alpha multiplier applied by BeginDisabled(). Multiply over current value of Alpha. ImVec2 WindowPadding; // Padding within a window. float WindowRounding; // Radius of window corners rounding. Set to 0.0f to have rectangular windows. Large values tend to lead to variety of artifacts and are not recommended. float WindowBorderSize; // Thickness of border around windows. Generally set to 0.0f or 1.0f. (Other values are not well tested and more CPU/GPU costly). @@ -1709,7 +1876,7 @@ struct ImGuiStyle bool AntiAliasedLinesUseTex; // Enable anti-aliased lines/borders using textures where possible. Require backend to render with bilinear filtering. Latched at the beginning of the frame (copied to ImDrawList). bool AntiAliasedFill; // Enable anti-aliased edges around filled shapes (rounded rectangles, circles, etc.). Disable if you are really tight on CPU/GPU. Latched at the beginning of the frame (copied to ImDrawList). float CurveTessellationTol; // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality. - float CircleSegmentMaxError; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry. + float CircleTessellationMaxError; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry. ImVec4 Colors[ImGuiCol_COUNT]; IMGUI_API ImGuiStyle(); @@ -1723,6 +1890,16 @@ struct ImGuiStyle // Access via ImGui::GetIO(). Read 'Programmer guide' section in .cpp file for general usage. //----------------------------------------------------------------------------- +// [Internal] Storage used by IsKeyDown(), IsKeyPressed() etc functions. +// If prior to 1.87 you used io.KeysDownDuration[] (which was marked as internal), you should use GetKeyData(key)->DownDuration and not io.KeysData[key]->DownDuration. +struct ImGuiKeyData +{ + bool Down; // True for if key is down + float DownDuration; // Duration the key has been down (<0.0f: not pressed, 0.0f: just pressed, >0.0f: time held) + float DownDurationPrev; // Last frame duration the key has been down + float AnalogValue; // 0.0f..1.0f for gamepad values +}; + struct ImGuiIO { //------------------------------------------------------------------ @@ -1731,15 +1908,14 @@ struct ImGuiIO ImGuiConfigFlags ConfigFlags; // = 0 // See ImGuiConfigFlags_ enum. Set by user/application. Gamepad/keyboard navigation options, etc. ImGuiBackendFlags BackendFlags; // = 0 // See ImGuiBackendFlags_ enum. Set by backend (imgui_impl_xxx files or custom backend) to communicate features supported by the backend. - ImVec2 DisplaySize; // // Main display size, in pixels. + ImVec2 DisplaySize; // // Main display size, in pixels (generally == GetMainViewport()->Size) float DeltaTime; // = 1.0f/60.0f // Time elapsed since last frame, in seconds. float IniSavingRate; // = 5.0f // Minimum time between saving positions/sizes to .ini file, in seconds. - const char* IniFilename; // = "imgui.ini" // Path to .ini file. Set NULL to disable automatic .ini loading/saving, if e.g. you want to manually load/save from memory. + const char* IniFilename; // = "imgui.ini" // Path to .ini file (important: default "imgui.ini" is relative to current working dir!). Set NULL to disable automatic .ini loading/saving or if you want to manually call LoadIniSettingsXXX() / SaveIniSettingsXXX() functions. const char* LogFilename; // = "imgui_log.txt"// Path to .log file (default parameter to ImGui::LogToFile when no file is specified). float MouseDoubleClickTime; // = 0.30f // Time for a double-click, in seconds. float MouseDoubleClickMaxDist; // = 6.0f // Distance threshold to stay in to validate a double-click, in pixels. float MouseDragThreshold; // = 6.0f // Distance threshold before considering we are dragging. - int KeyMap[ImGuiKey_COUNT]; // // Map of indices into the KeysDown[512] entries array which represent your "native" keyboard state. float KeyRepeatDelay; // = 0.250f // When holding a key/button, time before it starts repeating, in seconds (for buttons in Repeat mode, etc.). float KeyRepeatRate; // = 0.050f // When holding a key/button, rate at which it repeats, in seconds. void* UserData; // = NULL // Store your own data for retrieval by callbacks. @@ -1753,6 +1929,7 @@ struct ImGuiIO // Miscellaneous options bool MouseDrawCursor; // = false // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). Cannot be easily renamed to 'io.ConfigXXX' because this is frequently used by backend implementations. bool ConfigMacOSXBehaviors; // = defined(__APPLE__) // OS X style: Text editing cursor movement using Alt instead of Ctrl, Shortcuts using Cmd/Super instead of Ctrl, Line/Text Start and End using Cmd+Arrows instead of Home/End, Double click selects by word instead of selecting whole text, Multi-selection in lists uses Cmd/Super instead of Ctrl. + bool ConfigInputTrickleEventQueue; // = true // Enable input queue trickling: some types of events submitted during the same frame (e.g. button down + up) will be spread over multiple frames, improving interactions with low framerates. bool ConfigInputTextCursorBlink; // = true // Enable blinking cursor (optional as some users consider it to be distracting). bool ConfigDragClickToInputText; // = false // [BETA] Enable turning DragXXX widgets into text input with a simple mouse click-release (without moving). Not desirable on devices without a keyboard. bool ConfigWindowsResizeFromEdges; // = true // Enable resizing of windows from their edges and from the lower-left corner. This requires (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) because it needs mouse cursor feedback. (This used to be a per-window ImGuiWindowFlags_ResizeFromAnySide flag) @@ -1779,29 +1956,31 @@ struct ImGuiIO // Optional: Notify OS Input Method Editor of the screen position of your cursor for text input position (e.g. when using Japanese/Chinese IME on Windows) // (default to use native imm32 api on Windows) - void (*ImeSetInputScreenPosFn)(int x, int y); - void* ImeWindowHandle; // = NULL // (Windows) Set this to your HWND to get automatic IME cursor positioning. + void (*SetPlatformImeDataFn)(ImGuiViewport* viewport, ImGuiPlatformImeData* data); +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + void* ImeWindowHandle; // = NULL // [Obsolete] Set ImGuiViewport::PlatformHandleRaw instead. Set this to your HWND to get automatic IME cursor positioning. +#else + void* _UnusedPadding; // Unused field to keep data structure the same size. +#endif //------------------------------------------------------------------ - // Input - Fill before calling NewFrame() + // Input - Call before calling NewFrame() //------------------------------------------------------------------ - ImVec2 MousePos; // Mouse position, in pixels. Set to ImVec2(-FLT_MAX, -FLT_MAX) if mouse is unavailable (on another screen, etc.) - bool MouseDown[5]; // Mouse buttons: 0=left, 1=right, 2=middle + extras (ImGuiMouseButton_COUNT == 5). Dear ImGui mostly uses left and right buttons. Others buttons allows us to track if the mouse is being used by your application + available to user as a convenience via IsMouse** API. - float MouseWheel; // Mouse wheel Vertical: 1 unit scrolls about 5 lines text. - float MouseWheelH; // Mouse wheel Horizontal. Most users don't have a mouse with an horizontal wheel, may not be filled by all backends. - bool KeyCtrl; // Keyboard modifier pressed: Control - bool KeyShift; // Keyboard modifier pressed: Shift - bool KeyAlt; // Keyboard modifier pressed: Alt - bool KeySuper; // Keyboard modifier pressed: Cmd/Super/Windows - bool KeysDown[512]; // Keyboard keys that are pressed (ideally left in the "native" order your engine has access to keyboard keys, so you can use your own defines/enums for keys). - float NavInputs[ImGuiNavInput_COUNT]; // Gamepad inputs. Cleared back to zero by EndFrame(). Keyboard keys will be auto-mapped and be written here by NewFrame(). - - // Functions - IMGUI_API void AddInputCharacter(unsigned int c); // Queue new character input - IMGUI_API void AddInputCharacterUTF16(ImWchar16 c); // Queue new character input from an UTF-16 character, it can be a surrogate - IMGUI_API void AddInputCharactersUTF8(const char* str); // Queue new characters input from an UTF-8 string - IMGUI_API void ClearInputCharacters(); // Clear the text input buffer manually + // Input Functions + IMGUI_API void AddKeyEvent(ImGuiKey key, bool down); // Queue a new key down/up event. Key should be "translated" (as in, generally ImGuiKey_A matches the key end-user would use to emit an 'A' character) + IMGUI_API void AddKeyAnalogEvent(ImGuiKey key, bool down, float v); // Queue a new key down/up event for analog values (e.g. ImGuiKey_Gamepad_ values). Dead-zones should be handled by the backend. + IMGUI_API void AddMousePosEvent(float x, float y); // Queue a mouse position update. Use -FLT_MAX,-FLT_MAX to signify no mouse (e.g. app not focused and not hovered) + IMGUI_API void AddMouseButtonEvent(int button, bool down); // Queue a mouse button change + IMGUI_API void AddMouseWheelEvent(float wh_x, float wh_y); // Queue a mouse wheel update + IMGUI_API void AddFocusEvent(bool focused); // Queue a gain/loss of focus for the application (generally based on OS/platform focus of your window) + IMGUI_API void AddInputCharacter(unsigned int c); // Queue a new character input + IMGUI_API void AddInputCharacterUTF16(ImWchar16 c); // Queue a new character input from an UTF-16 character, it can be a surrogate + IMGUI_API void AddInputCharactersUTF8(const char* str); // Queue a new characters input from an UTF-8 string + + IMGUI_API void ClearInputCharacters(); // [Internal] Clear the text input buffer manually + IMGUI_API void ClearInputKeys(); // [Internal] Release all keys + IMGUI_API void SetKeyEventNativeData(ImGuiKey key, int native_keycode, int native_scancode, int native_legacy_index = -1); // [Optional] Specify index for legacy <1.87 IsKeyXXX() functions with native indices + specify native keycode, scancode. //------------------------------------------------------------------ // Output - Updated by NewFrame() or EndFrame()/Render() @@ -1809,45 +1988,71 @@ struct ImGuiIO // generally easier and more correct to use their state BEFORE calling NewFrame(). See FAQ for details!) //------------------------------------------------------------------ - bool WantCaptureMouse; // Set when Dear ImGui will use mouse inputs, in this case do not dispatch them to your main game/application (either way, always pass on mouse inputs to imgui). (e.g. unclicked mouse is hovering over an imgui window, widget is active, mouse was clicked over an imgui window, etc.). - bool WantCaptureKeyboard; // Set when Dear ImGui will use keyboard inputs, in this case do not dispatch them to your main game/application (either way, always pass keyboard inputs to imgui). (e.g. InputText active, or an imgui window is focused and navigation is enabled, etc.). - bool WantTextInput; // Mobile/console: when set, you may display an on-screen keyboard. This is set by Dear ImGui when it wants textual keyboard input to happen (e.g. when a InputText widget is active). - bool WantSetMousePos; // MousePos has been altered, backend should reposition mouse on next frame. Rarely used! Set only when ImGuiConfigFlags_NavEnableSetMousePos flag is enabled. - bool WantSaveIniSettings; // When manual .ini load/save is active (io.IniFilename == NULL), this will be set to notify your application that you can call SaveIniSettingsToMemory() and save yourself. Important: clear io.WantSaveIniSettings yourself after saving! - bool NavActive; // Keyboard/Gamepad navigation is currently allowed (will handle ImGuiKey_NavXXX events) = a window is focused and it doesn't use the ImGuiWindowFlags_NoNavInputs flag. - bool NavVisible; // Keyboard/Gamepad navigation is visible and allowed (will handle ImGuiKey_NavXXX events). - float Framerate; // Application framerate estimate, in frame per second. Solely for convenience. Rolling average estimation based on io.DeltaTime over 120 frames. - int MetricsRenderVertices; // Vertices output during last call to Render() - int MetricsRenderIndices; // Indices output during last call to Render() = number of triangles * 3 - int MetricsRenderWindows; // Number of visible windows - int MetricsActiveWindows; // Number of active windows - int MetricsActiveAllocations; // Number of active allocations, updated by MemAlloc/MemFree based on current context. May be off if you have multiple imgui contexts. - ImVec2 MouseDelta; // Mouse delta. Note that this is zero if either current or previous position are invalid (-FLT_MAX,-FLT_MAX), so a disappearing/reappearing mouse won't have a huge delta. + bool WantCaptureMouse; // Set when Dear ImGui will use mouse inputs, in this case do not dispatch them to your main game/application (either way, always pass on mouse inputs to imgui). (e.g. unclicked mouse is hovering over an imgui window, widget is active, mouse was clicked over an imgui window, etc.). + bool WantCaptureKeyboard; // Set when Dear ImGui will use keyboard inputs, in this case do not dispatch them to your main game/application (either way, always pass keyboard inputs to imgui). (e.g. InputText active, or an imgui window is focused and navigation is enabled, etc.). + bool WantTextInput; // Mobile/console: when set, you may display an on-screen keyboard. This is set by Dear ImGui when it wants textual keyboard input to happen (e.g. when a InputText widget is active). + bool WantSetMousePos; // MousePos has been altered, backend should reposition mouse on next frame. Rarely used! Set only when ImGuiConfigFlags_NavEnableSetMousePos flag is enabled. + bool WantSaveIniSettings; // When manual .ini load/save is active (io.IniFilename == NULL), this will be set to notify your application that you can call SaveIniSettingsToMemory() and save yourself. Important: clear io.WantSaveIniSettings yourself after saving! + bool NavActive; // Keyboard/Gamepad navigation is currently allowed (will handle ImGuiKey_NavXXX events) = a window is focused and it doesn't use the ImGuiWindowFlags_NoNavInputs flag. + bool NavVisible; // Keyboard/Gamepad navigation is visible and allowed (will handle ImGuiKey_NavXXX events). + float Framerate; // Rough estimate of application framerate, in frame per second. Solely for convenience. Rolling average estimation based on io.DeltaTime over 120 frames. + int MetricsRenderVertices; // Vertices output during last call to Render() + int MetricsRenderIndices; // Indices output during last call to Render() = number of triangles * 3 + int MetricsRenderWindows; // Number of visible windows + int MetricsActiveWindows; // Number of active windows + int MetricsActiveAllocations; // Number of active allocations, updated by MemAlloc/MemFree based on current context. May be off if you have multiple imgui contexts. + ImVec2 MouseDelta; // Mouse delta. Note that this is zero if either current or previous position are invalid (-FLT_MAX,-FLT_MAX), so a disappearing/reappearing mouse won't have a huge delta. + + // Legacy: before 1.87, we required backend to fill io.KeyMap[] (imgui->native map) during initialization and io.KeysDown[] (native indices) every frame. + // This is still temporarily supported as a legacy feature. However the new preferred scheme is for backend to call io.AddKeyEvent(). +#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO + int KeyMap[ImGuiKey_COUNT]; // [LEGACY] Input: map of indices into the KeysDown[512] entries array which represent your "native" keyboard state. The first 512 are now unused and should be kept zero. Legacy backend will write into KeyMap[] using ImGuiKey_ indices which are always >512. + bool KeysDown[512]; // [LEGACY] Input: Keyboard keys that are pressed (ideally left in the "native" order your engine has access to keyboard keys, so you can use your own defines/enums for keys). +#endif //------------------------------------------------------------------ // [Internal] Dear ImGui will maintain those fields. Forward compatibility not guaranteed! //------------------------------------------------------------------ - ImGuiKeyModFlags KeyMods; // Key mods flags (same as io.KeyCtrl/KeyShift/KeyAlt/KeySuper but merged into flags), updated by NewFrame() - ImVec2 MousePosPrev; // Previous mouse position (note that MouseDelta is not necessary == MousePos-MousePosPrev, in case either position is invalid) - ImVec2 MouseClickedPos[5]; // Position at time of clicking - double MouseClickedTime[5]; // Time of last click (used to figure out double-click) - bool MouseClicked[5]; // Mouse button went from !Down to Down - bool MouseDoubleClicked[5]; // Has mouse button been double-clicked? - bool MouseReleased[5]; // Mouse button went from Down to !Down - bool MouseDownOwned[5]; // Track if button was clicked inside a dear imgui window. We don't request mouse capture from the application if click started outside ImGui bounds. - bool MouseDownWasDoubleClick[5]; // Track if button down was a double-click - float MouseDownDuration[5]; // Duration the mouse button has been down (0.0f == just clicked) - float MouseDownDurationPrev[5]; // Previous time the mouse button has been down - ImVec2 MouseDragMaxDistanceAbs[5]; // Maximum distance, absolute, on each axis, of how much mouse has traveled from the clicking point - float MouseDragMaxDistanceSqr[5]; // Squared maximum distance of how much mouse has traveled from the clicking point - float KeysDownDuration[512]; // Duration the keyboard key has been down (0.0f == just pressed) - float KeysDownDurationPrev[512]; // Previous duration the key has been down + // Main Input State + // (this block used to be written by backend, since 1.87 it is best to NOT write to those directly, call the AddXXX functions above instead) + // (reading from those variables is fair game, as they are extremely unlikely to be moving anywhere) + ImVec2 MousePos; // Mouse position, in pixels. Set to ImVec2(-FLT_MAX, -FLT_MAX) if mouse is unavailable (on another screen, etc.) + bool MouseDown[5]; // Mouse buttons: 0=left, 1=right, 2=middle + extras (ImGuiMouseButton_COUNT == 5). Dear ImGui mostly uses left and right buttons. Others buttons allows us to track if the mouse is being used by your application + available to user as a convenience via IsMouse** API. + float MouseWheel; // Mouse wheel Vertical: 1 unit scrolls about 5 lines text. + float MouseWheelH; // Mouse wheel Horizontal. Most users don't have a mouse with an horizontal wheel, may not be filled by all backends. + bool KeyCtrl; // Keyboard modifier down: Control + bool KeyShift; // Keyboard modifier down: Shift + bool KeyAlt; // Keyboard modifier down: Alt + bool KeySuper; // Keyboard modifier down: Cmd/Super/Windows + float NavInputs[ImGuiNavInput_COUNT]; // Gamepad inputs. Cleared back to zero by EndFrame(). Keyboard keys will be auto-mapped and be written here by NewFrame(). + + // Other state maintained from data above + IO function calls + ImGuiKeyModFlags KeyMods; // Key mods flags (same as io.KeyCtrl/KeyShift/KeyAlt/KeySuper but merged into flags), updated by NewFrame() + ImGuiKeyModFlags KeyModsPrev; // Key mods flags (from previous frame) + ImGuiKeyData KeysData[ImGuiKey_KeysData_SIZE]; // Key state for all known keys. Use IsKeyXXX() functions to access this. + bool WantCaptureMouseUnlessPopupClose; // Alternative to WantCaptureMouse: (WantCaptureMouse == true && WantCaptureMouseUnlessPopupClose == false) when a click over void is expected to close a popup. + ImVec2 MousePosPrev; // Previous mouse position (note that MouseDelta is not necessary == MousePos-MousePosPrev, in case either position is invalid) + ImVec2 MouseClickedPos[5]; // Position at time of clicking + double MouseClickedTime[5]; // Time of last click (used to figure out double-click) + bool MouseClicked[5]; // Mouse button went from !Down to Down (same as MouseClickedCount[x] != 0) + bool MouseDoubleClicked[5]; // Has mouse button been double-clicked? (same as MouseClickedCount[x] == 2) + ImU16 MouseClickedCount[5]; // == 0 (not clicked), == 1 (same as MouseClicked[]), == 2 (double-clicked), == 3 (triple-clicked) etc. when going from !Down to Down + ImU16 MouseClickedLastCount[5]; // Count successive number of clicks. Stays valid after mouse release. Reset after another click is done. + bool MouseReleased[5]; // Mouse button went from Down to !Down + bool MouseDownOwned[5]; // Track if button was clicked inside a dear imgui window or over void blocked by a popup. We don't request mouse capture from the application if click started outside ImGui bounds. + bool MouseDownOwnedUnlessPopupClose[5]; //Track if button was clicked inside a dear imgui window. + float MouseDownDuration[5]; // Duration the mouse button has been down (0.0f == just clicked) + float MouseDownDurationPrev[5]; // Previous time the mouse button has been down + float MouseDragMaxDistanceSqr[5]; // Squared maximum distance of how much mouse has traveled from the clicking point (used for moving thresholds) float NavInputsDownDuration[ImGuiNavInput_COUNT]; float NavInputsDownDurationPrev[ImGuiNavInput_COUNT]; - float PenPressure; // Touch/Pen pressure (0.0f to 1.0f, should be >0.0f only when MouseDown[0] == true). Helper storage currently unused by Dear ImGui. - ImWchar16 InputQueueSurrogate; // For AddInputCharacterUTF16 - ImVector InputQueueCharacters; // Queue of _characters_ input (obtained by platform backend). Fill using AddInputCharacter() helper. + float PenPressure; // Touch/Pen pressure (0.0f to 1.0f, should be >0.0f only when MouseDown[0] == true). Helper storage currently unused by Dear ImGui. + bool AppFocusLost; + ImS8 BackendUsingLegacyKeyArrays; // -1: unknown, 0: using AddKeyEvent(), 1: using legacy io.KeysDown[] + bool BackendUsingLegacyNavInputArray; // 0: using AddKeyAnalogEvent(), 1: writing to legacy io.NavInputs[] directly + ImWchar16 InputQueueSurrogate; // For AddInputCharacterUTF16() + ImVector InputQueueCharacters; // Queue of _characters_ input (obtained by platform backend). Fill using AddInputCharacter() helper. IMGUI_API ImGuiIO(); }; @@ -1950,47 +2155,6 @@ struct ImGuiTableSortSpecs ImGuiTableSortSpecs() { memset(this, 0, sizeof(*this)); } }; -//----------------------------------------------------------------------------- -// [SECTION] Obsolete functions -// (Will be removed! Read 'API BREAKING CHANGES' section in imgui.cpp for details) -// Please keep your copy of dear imgui up to date! Occasionally set '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' in imconfig.h to stay ahead. -//----------------------------------------------------------------------------- - -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS -namespace ImGui -{ - // OBSOLETED in 1.79 (from August 2020) - static inline void OpenPopupContextItem(const char* str_id = NULL, ImGuiMouseButton mb = 1) { OpenPopupOnItemClick(str_id, mb); } // Bool return value removed. Use IsWindowAppearing() in BeginPopup() instead. Renamed in 1.77, renamed back in 1.79. Sorry! - // OBSOLETED in 1.78 (from June 2020) - // Old drag/sliders functions that took a 'float power = 1.0' argument instead of flags. - // For shared code, you can version check at compile-time with `#if IMGUI_VERSION_NUM >= 17704`. - IMGUI_API bool DragScalar(const char* label, ImGuiDataType data_type, void* p_data, float v_speed, const void* p_min, const void* p_max, const char* format, float power); - IMGUI_API bool DragScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, float v_speed, const void* p_min, const void* p_max, const char* format, float power); - static inline bool DragFloat(const char* label, float* v, float v_speed, float v_min, float v_max, const char* format, float power) { return DragScalar(label, ImGuiDataType_Float, v, v_speed, &v_min, &v_max, format, power); } - static inline bool DragFloat2(const char* label, float v[2], float v_speed, float v_min, float v_max, const char* format, float power) { return DragScalarN(label, ImGuiDataType_Float, v, 2, v_speed, &v_min, &v_max, format, power); } - static inline bool DragFloat3(const char* label, float v[3], float v_speed, float v_min, float v_max, const char* format, float power) { return DragScalarN(label, ImGuiDataType_Float, v, 3, v_speed, &v_min, &v_max, format, power); } - static inline bool DragFloat4(const char* label, float v[4], float v_speed, float v_min, float v_max, const char* format, float power) { return DragScalarN(label, ImGuiDataType_Float, v, 4, v_speed, &v_min, &v_max, format, power); } - IMGUI_API bool SliderScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format, float power); - IMGUI_API bool SliderScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, const void* p_min, const void* p_max, const char* format, float power); - static inline bool SliderFloat(const char* label, float* v, float v_min, float v_max, const char* format, float power) { return SliderScalar(label, ImGuiDataType_Float, v, &v_min, &v_max, format, power); } - static inline bool SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* format, float power) { return SliderScalarN(label, ImGuiDataType_Float, v, 2, &v_min, &v_max, format, power); } - static inline bool SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* format, float power) { return SliderScalarN(label, ImGuiDataType_Float, v, 3, &v_min, &v_max, format, power); } - static inline bool SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* format, float power) { return SliderScalarN(label, ImGuiDataType_Float, v, 4, &v_min, &v_max, format, power); } - // OBSOLETED in 1.77 (from June 2020) - static inline bool BeginPopupContextWindow(const char* str_id, ImGuiMouseButton mb, bool over_items) { return BeginPopupContextWindow(str_id, mb | (over_items ? 0 : ImGuiPopupFlags_NoOpenOverItems)); } - // OBSOLETED in 1.72 (from April 2019) - static inline void TreeAdvanceToLabelPos() { SetCursorPosX(GetCursorPosX() + GetTreeNodeToLabelSpacing()); } - // OBSOLETED in 1.71 (from June 2019) - static inline void SetNextTreeNodeOpen(bool open, ImGuiCond cond = 0) { SetNextItemOpen(open, cond); } - // OBSOLETED in 1.70 (from May 2019) - static inline float GetContentRegionAvailWidth() { return GetContentRegionAvail().x; } - // OBSOLETED in 1.69 (from Mar 2019) - static inline ImDrawList* GetOverlayDrawList() { return GetForegroundDrawList(); } - // OBSOLETED in 1.66 (from Sep 2018) - static inline void SetScrollHere(float center_ratio=0.5f){ SetScrollHereY(center_ratio); } -} -#endif - //----------------------------------------------------------------------------- // [SECTION] Helpers (ImGuiOnceUponAFrame, ImGuiTextFilter, ImGuiTextBuffer, ImGuiStorage, ImGuiListClipper, ImColor) //----------------------------------------------------------------------------- @@ -2111,10 +2275,12 @@ struct ImGuiStorage }; // Helper: Manually clip large list of items. -// If you are submitting lots of evenly spaced items and you have a random access to the list, you can perform coarse -// clipping based on visibility to save yourself from processing those items at all. +// If you have lots evenly spaced items and you have a random access to the list, you can perform coarse +// clipping based on visibility to only submit items that are in view. // The clipper calculates the range of visible items and advance the cursor to compensate for the non-visible items we have skipped. -// (Dear ImGui already clip items based on their bounds but it needs to measure text size to do so, whereas manual coarse clipping before submission makes this cost and your own data fetching/submission cost almost null) +// (Dear ImGui already clip items based on their bounds but: it needs to first layout the item to do so, and generally +// fetching/submitting your own data incurs additional cost. Coarse clipping using ImGuiListClipper allows you to easily +// scale using lists with tens of thousands of items without a problem) // Usage: // ImGuiListClipper clipper; // clipper.Begin(1000); // We have 1000 elements, evenly spaced. @@ -2123,30 +2289,30 @@ struct ImGuiStorage // ImGui::Text("line number %d", i); // Generally what happens is: // - Clipper lets you process the first element (DisplayStart = 0, DisplayEnd = 1) regardless of it being visible or not. -// - User code submit one element. +// - User code submit that one element. // - Clipper can measure the height of the first element // - Clipper calculate the actual range of elements to display based on the current clipping rectangle, position the cursor before the first visible element. // - User code submit visible elements. +// - The clipper also handles various subtleties related to keyboard/gamepad navigation, wrapping etc. struct ImGuiListClipper { - int DisplayStart; - int DisplayEnd; - - // [Internal] - int ItemsCount; - int StepNo; - int ItemsFrozen; - float ItemsHeight; - float StartPosY; + int DisplayStart; // First item to display, updated by each call to Step() + int DisplayEnd; // End of items to display (exclusive) + int ItemsCount; // [Internal] Number of items + float ItemsHeight; // [Internal] Height of item after a first step and item submission can calculate it + float StartPosY; // [Internal] Cursor position at the time of Begin() or after table frozen rows are all processed + void* TempData; // [Internal] Internal data + // items_count: Use INT_MAX if you don't know how many items you have (in which case the cursor won't be advanced in the final step) + // items_height: Use -1.0f to be calculated automatically on first step. Otherwise pass in the distance between your items, typically GetTextLineHeightWithSpacing() or GetFrameHeightWithSpacing(). IMGUI_API ImGuiListClipper(); IMGUI_API ~ImGuiListClipper(); + IMGUI_API void Begin(int items_count, float items_height = -1.0f); + IMGUI_API void End(); // Automatically called on the last call of Step() that returns false. + IMGUI_API bool Step(); // Call until it returns false. The DisplayStart/DisplayEnd fields will be set and you can process/draw those items. - // items_count: Use INT_MAX if you don't know how many items you have (in which case the cursor won't be advanced in the final step) - // items_height: Use -1.0f to be calculated automatically on first step. Otherwise pass in the distance between your items, typically GetTextLineHeightWithSpacing() or GetFrameHeightWithSpacing(). - IMGUI_API void Begin(int items_count, float items_height = -1.0f); // Automatically called by constructor if you passed 'items_count' or by Step() in Step 1. - IMGUI_API void End(); // Automatically called on the last call of Step() that returns false. - IMGUI_API bool Step(); // Call until it returns false. The DisplayStart/DisplayEnd fields will be set and you can process/draw those items. + // Call ForceDisplayRangeByIndices() before first call to Step() if you need a range of items to be displayed regardless of visibility. + IMGUI_API void ForceDisplayRangeByIndices(int item_min, int item_max); // item_max is exclusive e.g. use (42, 42+1) to make item 42 always visible BUT due to alignment/padding of certain items it is likely that an extra item may be included on either end of the display range. #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS inline ImGuiListClipper(int items_count, float items_height = -1.0f) { memset(this, 0, sizeof(*this)); ItemsCount = -1; Begin(items_count, items_height); } // [removed in 1.79] @@ -2221,29 +2387,25 @@ typedef void (*ImDrawCallback)(const ImDrawList* parent_list, const ImDrawCmd* c #define ImDrawCallback_ResetRenderState (ImDrawCallback)(-1) // Typically, 1 command = 1 GPU draw call (unless command is a callback) -// - VtxOffset/IdxOffset: When 'io.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset' is enabled, -// those fields allow us to render meshes larger than 64K vertices while keeping 16-bit indices. -// Pre-1.71 backends will typically ignore the VtxOffset/IdxOffset fields. +// - VtxOffset: When 'io.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset' is enabled, +// this fields allow us to render meshes larger than 64K vertices while keeping 16-bit indices. +// Backends made for <1.71. will typically ignore the VtxOffset fields. // - The ClipRect/TextureId/VtxOffset fields must be contiguous as we memcmp() them together (this is asserted for). struct ImDrawCmd { ImVec4 ClipRect; // 4*4 // Clipping rectangle (x1, y1, x2, y2). Subtract ImDrawData->DisplayPos to get clipping rectangle in "viewport" coordinates ImTextureID TextureId; // 4-8 // User-provided texture ID. Set by user in ImfontAtlas::SetTexID() for fonts or passed to Image*() functions. Ignore if never using images or multiple fonts atlas. unsigned int VtxOffset; // 4 // Start offset in vertex buffer. ImGuiBackendFlags_RendererHasVtxOffset: always 0, otherwise may be >0 to support meshes larger than 64K vertices with 16-bit indices. - unsigned int IdxOffset; // 4 // Start offset in index buffer. Always equal to sum of ElemCount drawn so far. + unsigned int IdxOffset; // 4 // Start offset in index buffer. unsigned int ElemCount; // 4 // Number of indices (multiple of 3) to be rendered as triangles. Vertices are stored in the callee ImDrawList's vtx_buffer[] array, indices in idx_buffer[]. ImDrawCallback UserCallback; // 4-8 // If != NULL, call the function instead of rendering the vertices. clip_rect and texture_id will be set normally. void* UserCallbackData; // 4-8 // The draw callback code can access this. ImDrawCmd() { memset(this, 0, sizeof(*this)); } // Also ensure our padding fields are zeroed -}; -// Vertex index, default to 16-bit -// To allow large meshes with 16-bit indices: set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset' and handle ImDrawCmd::VtxOffset in the renderer backend (recommended). -// To use 32-bit indices: override with '#define ImDrawIdx unsigned int' in imconfig.h. -#ifndef ImDrawIdx -typedef unsigned short ImDrawIdx; -#endif + // Since 1.83: returns ImTextureID associated with this draw call. Warning: DO NOT assume this is always same as 'TextureId' (we will change this function for an upcoming feature) + inline ImTextureID GetTexID() const { return TextureId; } +}; // Vertex layout #ifndef IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT @@ -2294,21 +2456,27 @@ struct ImDrawListSplitter IMGUI_API void SetCurrentChannel(ImDrawList* draw_list, int channel_idx); }; -enum ImDrawCornerFlags_ -{ - ImDrawCornerFlags_None = 0, - ImDrawCornerFlags_TopLeft = 1 << 0, // 0x1 - ImDrawCornerFlags_TopRight = 1 << 1, // 0x2 - ImDrawCornerFlags_BotLeft = 1 << 2, // 0x4 - ImDrawCornerFlags_BotRight = 1 << 3, // 0x8 - ImDrawCornerFlags_Top = ImDrawCornerFlags_TopLeft | ImDrawCornerFlags_TopRight, // 0x3 - ImDrawCornerFlags_Bot = ImDrawCornerFlags_BotLeft | ImDrawCornerFlags_BotRight, // 0xC - ImDrawCornerFlags_Left = ImDrawCornerFlags_TopLeft | ImDrawCornerFlags_BotLeft, // 0x5 - ImDrawCornerFlags_Right = ImDrawCornerFlags_TopRight | ImDrawCornerFlags_BotRight, // 0xA - ImDrawCornerFlags_All = 0xF // In your function calls you may use ~0 (= all bits sets) instead of ImDrawCornerFlags_All, as a convenience +// Flags for ImDrawList functions +// (Legacy: bit 0 must always correspond to ImDrawFlags_Closed to be backward compatible with old API using a bool. Bits 1..3 must be unused) +enum ImDrawFlags_ +{ + ImDrawFlags_None = 0, + ImDrawFlags_Closed = 1 << 0, // PathStroke(), AddPolyline(): specify that shape should be closed (Important: this is always == 1 for legacy reason) + ImDrawFlags_RoundCornersTopLeft = 1 << 4, // AddRect(), AddRectFilled(), PathRect(): enable rounding top-left corner only (when rounding > 0.0f, we default to all corners). Was 0x01. + ImDrawFlags_RoundCornersTopRight = 1 << 5, // AddRect(), AddRectFilled(), PathRect(): enable rounding top-right corner only (when rounding > 0.0f, we default to all corners). Was 0x02. + ImDrawFlags_RoundCornersBottomLeft = 1 << 6, // AddRect(), AddRectFilled(), PathRect(): enable rounding bottom-left corner only (when rounding > 0.0f, we default to all corners). Was 0x04. + ImDrawFlags_RoundCornersBottomRight = 1 << 7, // AddRect(), AddRectFilled(), PathRect(): enable rounding bottom-right corner only (when rounding > 0.0f, we default to all corners). Wax 0x08. + ImDrawFlags_RoundCornersNone = 1 << 8, // AddRect(), AddRectFilled(), PathRect(): disable rounding on all corners (when rounding > 0.0f). This is NOT zero, NOT an implicit flag! + ImDrawFlags_RoundCornersTop = ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersTopRight, + ImDrawFlags_RoundCornersBottom = ImDrawFlags_RoundCornersBottomLeft | ImDrawFlags_RoundCornersBottomRight, + ImDrawFlags_RoundCornersLeft = ImDrawFlags_RoundCornersBottomLeft | ImDrawFlags_RoundCornersTopLeft, + ImDrawFlags_RoundCornersRight = ImDrawFlags_RoundCornersBottomRight | ImDrawFlags_RoundCornersTopRight, + ImDrawFlags_RoundCornersAll = ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersTopRight | ImDrawFlags_RoundCornersBottomLeft | ImDrawFlags_RoundCornersBottomRight, + ImDrawFlags_RoundCornersDefault_ = ImDrawFlags_RoundCornersAll, // Default to ALL corners if none of the _RoundCornersXX flags are specified. + ImDrawFlags_RoundCornersMask_ = ImDrawFlags_RoundCornersAll | ImDrawFlags_RoundCornersNone }; -// Flags for ImDrawList. Those are set automatically by ImGui:: functions from ImGuiIO settings, and generally not manipulated directly. +// Flags for ImDrawList instance. Those are set automatically by ImGui:: functions from ImGuiIO settings, and generally not manipulated directly. // It is however possible to temporarily alter flags between calls to ImDrawList:: functions. enum ImDrawListFlags_ { @@ -2325,7 +2493,8 @@ enum ImDrawListFlags_ // Each dear imgui window contains its own ImDrawList. You can use ImGui::GetWindowDrawList() to // access the current window draw list and draw custom primitives. // You can interleave normal ImGui:: calls and adding primitives to the current draw list. -// All positions are generally in pixel coordinates (top-left at (0,0), bottom-right at io.DisplaySize), but you are totally free to apply whatever transformation matrix to want to the data (if you apply such transformation you'll want to apply it to ClipRect as well) +// In single viewport mode, top-left is == GetMainViewport()->Pos (generally 0,0), bottom-right is == GetMainViewport()->Pos+Size (generally io.DisplaySize). +// You are totally free to apply whatever transformation matrix to want to the data (depending on the use of the transformation you may want to apply it to ClipRect as well!) // Important: Primitives are always added to the list and not culled (culling is done at higher-level by ImGui:: functions), if you use this API a lot consider coarse culling your drawn objects. struct ImDrawList { @@ -2367,8 +2536,8 @@ struct ImDrawList // In future versions we will use textures to provide cheaper and higher-quality circles. // Use AddNgon() and AddNgonFilled() functions if you need to guaranteed a specific number of sides. IMGUI_API void AddLine(const ImVec2& p1, const ImVec2& p2, ImU32 col, float thickness = 1.0f); - IMGUI_API void AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding = 0.0f, ImDrawCornerFlags rounding_corners = ImDrawCornerFlags_All, float thickness = 1.0f); // a: upper-left, b: lower-right (== upper-left + size), rounding_corners_flags: 4 bits corresponding to which corner to round - IMGUI_API void AddRectFilled(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding = 0.0f, ImDrawCornerFlags rounding_corners = ImDrawCornerFlags_All); // a: upper-left, b: lower-right (== upper-left + size) + IMGUI_API void AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding = 0.0f, ImDrawFlags flags = 0, float thickness = 1.0f); // a: upper-left, b: lower-right (== upper-left + size) + IMGUI_API void AddRectFilled(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding = 0.0f, ImDrawFlags flags = 0); // a: upper-left, b: lower-right (== upper-left + size) IMGUI_API void AddRectFilledMultiColor(const ImVec2& p_min, const ImVec2& p_max, ImU32 col_upr_left, ImU32 col_upr_right, ImU32 col_bot_right, ImU32 col_bot_left); IMGUI_API void AddQuad(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness = 1.0f); IMGUI_API void AddQuadFilled(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col); @@ -2380,7 +2549,7 @@ struct ImDrawList IMGUI_API void AddNgonFilled(const ImVec2& center, float radius, ImU32 col, int num_segments); IMGUI_API void AddText(const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL); IMGUI_API void AddText(const ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL, float wrap_width = 0.0f, const ImVec4* cpu_fine_clip_rect = NULL); - IMGUI_API void AddPolyline(const ImVec2* points, int num_points, ImU32 col, bool closed, float thickness); + IMGUI_API void AddPolyline(const ImVec2* points, int num_points, ImU32 col, ImDrawFlags flags, float thickness); IMGUI_API void AddConvexPolyFilled(const ImVec2* points, int num_points, ImU32 col); // Note: Anti-aliased filling requires points to be in clockwise order. IMGUI_API void AddBezierCubic(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments = 0); // Cubic Bezier (4 control points) IMGUI_API void AddBezierQuadratic(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col, float thickness, int num_segments = 0); // Quadratic Bezier (3 control points) @@ -2391,19 +2560,19 @@ struct ImDrawList // - "uv_min" and "uv_max" represent the normalized texture coordinates to use for those corners. Using (0,0)->(1,1) texture coordinates will generally display the entire texture. IMGUI_API void AddImage(ImTextureID user_texture_id, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min = ImVec2(0, 0), const ImVec2& uv_max = ImVec2(1, 1), ImU32 col = IM_COL32_WHITE); IMGUI_API void AddImageQuad(ImTextureID user_texture_id, const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& uv1 = ImVec2(0, 0), const ImVec2& uv2 = ImVec2(1, 0), const ImVec2& uv3 = ImVec2(1, 1), const ImVec2& uv4 = ImVec2(0, 1), ImU32 col = IM_COL32_WHITE); - IMGUI_API void AddImageRounded(ImTextureID user_texture_id, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min, const ImVec2& uv_max, ImU32 col, float rounding, ImDrawCornerFlags rounding_corners = ImDrawCornerFlags_All); + IMGUI_API void AddImageRounded(ImTextureID user_texture_id, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min, const ImVec2& uv_max, ImU32 col, float rounding, ImDrawFlags flags = 0); // Stateful path API, add points then finish with PathFillConvex() or PathStroke() inline void PathClear() { _Path.Size = 0; } inline void PathLineTo(const ImVec2& pos) { _Path.push_back(pos); } inline void PathLineToMergeDuplicate(const ImVec2& pos) { if (_Path.Size == 0 || memcmp(&_Path.Data[_Path.Size - 1], &pos, 8) != 0) _Path.push_back(pos); } inline void PathFillConvex(ImU32 col) { AddConvexPolyFilled(_Path.Data, _Path.Size, col); _Path.Size = 0; } // Note: Anti-aliased filling requires points to be in clockwise order. - inline void PathStroke(ImU32 col, bool closed, float thickness = 1.0f) { AddPolyline(_Path.Data, _Path.Size, col, closed, thickness); _Path.Size = 0; } - IMGUI_API void PathArcTo(const ImVec2& center, float radius, float a_min, float a_max, int num_segments = 10); - IMGUI_API void PathArcToFast(const ImVec2& center, float radius, int a_min_of_12, int a_max_of_12); // Use precomputed angles for a 12 steps circle - IMGUI_API void PathBezierCubicCurveTo(const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, int num_segments = 0); // Cubic Bezier (4 control points) - IMGUI_API void PathBezierQuadraticCurveTo(const ImVec2& p2, const ImVec2& p3, int num_segments = 0); // Quadratic Bezier (3 control points) - IMGUI_API void PathRect(const ImVec2& rect_min, const ImVec2& rect_max, float rounding = 0.0f, ImDrawCornerFlags rounding_corners = ImDrawCornerFlags_All); + inline void PathStroke(ImU32 col, ImDrawFlags flags = 0, float thickness = 1.0f) { AddPolyline(_Path.Data, _Path.Size, col, flags, thickness); _Path.Size = 0; } + IMGUI_API void PathArcTo(const ImVec2& center, float radius, float a_min, float a_max, int num_segments = 0); + IMGUI_API void PathArcToFast(const ImVec2& center, float radius, int a_min_of_12, int a_max_of_12); // Use precomputed angles for a 12 steps circle + IMGUI_API void PathBezierCubicCurveTo(const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, int num_segments = 0); // Cubic Bezier (4 control points) + IMGUI_API void PathBezierQuadraticCurveTo(const ImVec2& p2, const ImVec2& p3, int num_segments = 0); // Quadratic Bezier (3 control points) + IMGUI_API void PathRect(const ImVec2& rect_min, const ImVec2& rect_max, float rounding = 0.0f, ImDrawFlags flags = 0); // Advanced IMGUI_API void AddCallback(ImDrawCallback callback, void* callback_data); // Your rendering function must check for 'UserCallback' in ImDrawCmd and call the function instead of rendering triangles. @@ -2433,17 +2602,21 @@ struct ImDrawList inline void PrimVtx(const ImVec2& pos, const ImVec2& uv, ImU32 col) { PrimWriteIdx((ImDrawIdx)_VtxCurrentIdx); PrimWriteVtx(pos, uv, col); } // Write vertex with unique index #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - inline void AddBezierCurve(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments = 0) { AddBezierCubic(p1, p2, p3, p4, col, thickness, num_segments); } - inline void PathBezierCurveTo(const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, int num_segments = 0) { PathBezierCubicCurveTo(p2, p3, p4, num_segments); } + inline void AddBezierCurve(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments = 0) { AddBezierCubic(p1, p2, p3, p4, col, thickness, num_segments); } // OBSOLETED in 1.80 (Jan 2021) + inline void PathBezierCurveTo(const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, int num_segments = 0) { PathBezierCubicCurveTo(p2, p3, p4, num_segments); } // OBSOLETED in 1.80 (Jan 2021) #endif // [Internal helpers] IMGUI_API void _ResetForNewFrame(); IMGUI_API void _ClearFreeMemory(); IMGUI_API void _PopUnusedDrawCmd(); + IMGUI_API void _TryMergeDrawCmds(); IMGUI_API void _OnChangedClipRect(); IMGUI_API void _OnChangedTextureID(); IMGUI_API void _OnChangedVtxOffset(); + IMGUI_API int _CalcCircleAutoSegmentCount(float radius) const; + IMGUI_API void _PathArcToFastEx(const ImVec2& center, float radius, int a_min_sample, int a_max_sample, int a_step); + IMGUI_API void _PathArcToN(const ImVec2& center, float radius, float a_min, float a_max, int num_segments); }; // All draw data to render a Dear ImGui frame @@ -2452,18 +2625,17 @@ struct ImDrawList struct ImDrawData { bool Valid; // Only valid after Render() is called and before the next NewFrame() is called. - ImDrawList** CmdLists; // Array of ImDrawList* to render. The ImDrawList are owned by ImGuiContext and only pointed to from here. int CmdListsCount; // Number of ImDrawList* to render int TotalIdxCount; // For convenience, sum of all ImDrawList's IdxBuffer.Size int TotalVtxCount; // For convenience, sum of all ImDrawList's VtxBuffer.Size - ImVec2 DisplayPos; // Upper-left position of the viewport to render (== upper-left of the orthogonal projection matrix to use) - ImVec2 DisplaySize; // Size of the viewport to render (== io.DisplaySize for the main viewport) (DisplayPos + DisplaySize == lower-right of the orthogonal projection matrix to use) + ImDrawList** CmdLists; // Array of ImDrawList* to render. The ImDrawList are owned by ImGuiContext and only pointed to from here. + ImVec2 DisplayPos; // Top-left position of the viewport to render (== top-left of the orthogonal projection matrix to use) (== GetMainViewport()->Pos for the main viewport, == (0.0) in most single-viewport applications) + ImVec2 DisplaySize; // Size of the viewport to render (== GetMainViewport()->Size for the main viewport, == io.DisplaySize in most single-viewport applications) ImVec2 FramebufferScale; // Amount of pixels for each unit of DisplaySize. Based on io.DisplayFramebufferScale. Generally (1,1) on normal display, (2,2) on OSX with Retina display. // Functions - ImDrawData() { Valid = false; Clear(); } - ~ImDrawData() { Clear(); } - void Clear() { Valid = false; CmdLists = NULL; CmdListsCount = TotalVtxCount = TotalIdxCount = 0; DisplayPos = DisplaySize = FramebufferScale = ImVec2(0.f, 0.f); } // The ImDrawList are owned by ImGuiContext! + ImDrawData() { Clear(); } + void Clear() { memset(this, 0, sizeof(*this)); } // The ImDrawList are owned by ImGuiContext! IMGUI_API void DeIndexAllBuffers(); // Helper to convert all buffers from indexed to non-indexed, in case you cannot render indexed. Note: this is slow and most likely a waste of resources. Always prefer indexed rendering! IMGUI_API void ScaleClipRects(const ImVec2& fb_scale); // Helper to scale the ClipRect field of each ImDrawCmd. Use if your final output buffer is at a different scale than Dear ImGui expects, or if there is a difference between your window resolution and framebuffer resolution. }; @@ -2479,8 +2651,8 @@ struct ImFontConfig bool FontDataOwnedByAtlas; // true // TTF/OTF data ownership taken by the container ImFontAtlas (will delete memory itself). int FontNo; // 0 // Index of font within TTF/OTF file float SizePixels; // // Size in pixels for rasterizer (more or less maps to the resulting font height). - int OversampleH; // 3 // Rasterize at higher quality for sub-pixel positioning. Read https://github.com/nothings/stb/blob/master/tests/oversample/README.md for details. - int OversampleV; // 1 // Rasterize at higher quality for sub-pixel positioning. We don't use sub-pixel positions on the Y axis. + int OversampleH; // 3 // Rasterize at higher quality for sub-pixel positioning. Note the difference between 2 and 3 is minimal so you can reduce this to 2 to save memory. Read https://github.com/nothings/stb/blob/master/tests/oversample/README.md for details. + int OversampleV; // 1 // Rasterize at higher quality for sub-pixel positioning. This is not really useful as we don't use sub-pixel positions on the Y axis. bool PixelSnapH; // false // Align every glyph to pixel boundary. Useful e.g. if you are merging a non-pixel aligned font with the default font. If enabled, you can set OversampleH/V to 1. ImVec2 GlyphExtraSpacing; // 0, 0 // Extra spacing (in pixels) between glyphs. Only X axis is supported for now. ImVec2 GlyphOffset; // 0, 0 // Offset all glyphs from this font input. @@ -2488,7 +2660,7 @@ struct ImFontConfig float GlyphMinAdvanceX; // 0 // Minimum AdvanceX for glyphs, set Min to align font icons, set both Min/Max to enforce mono-space font float GlyphMaxAdvanceX; // FLT_MAX // Maximum AdvanceX for glyphs bool MergeMode; // false // Merge into previous ImFont, so you can combine multiple inputs font into one ImFont (e.g. ASCII font + icons + Japanese glyphs). You may want to use GlyphOffset.y when merge font of different heights. - unsigned int RasterizerFlags; // 0x00 // Settings for custom font rasterizer (e.g. ImGuiFreeType). Leave as zero if you aren't using one. + unsigned int FontBuilderFlags; // 0 // Settings for custom font builder. THIS IS BUILDER IMPLEMENTATION DEPENDENT. Leave as zero if unsure. float RasterizerMultiply; // 1.0f // Brighten (>1.0f) or darken (<1.0f) font output. Brightening small fonts may be a good workaround to make them more readable. ImWchar EllipsisChar; // -1 // Explicitly specify unicode codepoint of ellipsis character. When fonts are being merged first specified ellipsis will be used. @@ -2503,8 +2675,9 @@ struct ImFontConfig // (Note: some language parsers may fail to convert the 31+1 bitfield members, in this case maybe drop store a single u32 or we can rework this) struct ImFontGlyph { - unsigned int Codepoint : 31; // 0x0000..0xFFFF - unsigned int Visible : 1; // Flag to allow early out when rendering + unsigned int Colored : 1; // Flag to indicate glyph is colored and should generally ignore tinting (make it usable with no shift on little-endian as this is used in loops) + unsigned int Visible : 1; // Flag to indicate glyph has no visible pixels (e.g. space). Allow early out when rendering. + unsigned int Codepoint : 30; // 0x0000..0x10FFFF float AdvanceX; // Distance to next character (= data from font + ImFontConfig::GlyphExtraSpacing.x baked in) float X0, Y0, X1, Y1; // Glyph corners float U0, V0, U1, V1; // Texture coordinates @@ -2588,7 +2761,7 @@ struct ImFontAtlas IMGUI_API bool Build(); // Build pixels data. This is called automatically for you by the GetTexData*** functions. IMGUI_API void GetTexDataAsAlpha8(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel = NULL); // 1 byte per-pixel IMGUI_API void GetTexDataAsRGBA32(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel = NULL); // 4 bytes-per-pixel - bool IsBuilt() const { return Fonts.Size > 0 && (TexPixelsAlpha8 != NULL || TexPixelsRGBA32 != NULL); } + bool IsBuilt() const { return Fonts.Size > 0 && TexReady; } // Bit ambiguous: used to detect when user didn't built texture but effectively we should check TexID != 0 except that would be backend dependent... void SetTexID(ImTextureID id) { TexID = id; } //------------------------------------------- @@ -2612,11 +2785,12 @@ struct ImFontAtlas //------------------------------------------- // You can request arbitrary rectangles to be packed into the atlas, for your own purposes. - // After calling Build(), you can query the rectangle position and render your pixels. - // You can also request your rectangles to be mapped as font glyph (given a font + Unicode point), - // so you can render e.g. custom colorful icons and use them as regular glyphs. - // Read docs/FONTS.md for more details about using colorful icons. - // Note: this API may be redesigned later in order to support multi-monitor varying DPI settings. + // - After calling Build(), you can query the rectangle position and render your pixels. + // - If you render colored output, set 'atlas->TexPixelsUseColors = true' as this may help some backends decide of prefered texture format. + // - You can also request your rectangles to be mapped as font glyph (given a font + Unicode point), + // so you can render e.g. custom colorful icons and use them as regular glyphs. + // - Read docs/FONTS.md for more details about using colorful icons. + // - Note: this API may be redesigned later in order to support multi-monitor varying DPI settings. IMGUI_API int AddCustomRectRegular(int width, int height); IMGUI_API int AddCustomRectFontGlyph(ImFont* font, ImWchar id, int width, int height, float advance_x, const ImVec2& offset = ImVec2(0, 0)); ImFontAtlasCustomRect* GetCustomRectByIndex(int index) { IM_ASSERT(index >= 0); return &CustomRects[index]; } @@ -2629,14 +2803,16 @@ struct ImFontAtlas // Members //------------------------------------------- - bool Locked; // Marked as Locked by ImGui::NewFrame() so attempt to modify the atlas will assert. ImFontAtlasFlags Flags; // Build flags (see ImFontAtlasFlags_) ImTextureID TexID; // User data to refer to the texture once it has been uploaded to user's graphic systems. It is passed back to you during rendering via the ImDrawCmd structure. int TexDesiredWidth; // Texture width desired by user before Build(). Must be a power-of-two. If have many glyphs your graphics API have texture size restrictions you may want to increase texture width to decrease height. int TexGlyphPadding; // Padding between glyphs within texture in pixels. Defaults to 1. If your rendering method doesn't rely on bilinear filtering you may set this to 0. + bool Locked; // Marked as Locked by ImGui::NewFrame() so attempt to modify the atlas will assert. // [Internal] // NB: Access texture data via GetTexData*() calls! Which will setup a default font for you. + bool TexReady; // Set when texture was built matching current font input + bool TexPixelsUseColors; // Tell whether our texture data is known to use colors (rather than just alpha channel), in order to help backend select a format. unsigned char* TexPixelsAlpha8; // 1 component per pixel, each component is unsigned 8-bit. Total size = TexWidth * TexHeight unsigned int* TexPixelsRGBA32; // 4 component per pixel, each component is unsigned 8-bit. Total size = TexWidth * TexHeight * 4 int TexWidth; // Texture width calculated during Build(). @@ -2648,14 +2824,17 @@ struct ImFontAtlas ImVector ConfigData; // Configuration data ImVec4 TexUvLines[IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 1]; // UVs for baked anti-aliased lines + // [Internal] Font builder + const ImFontBuilderIO* FontBuilderIO; // Opaque interface to a font builder (default to stb_truetype, can be changed to use FreeType by defining IMGUI_ENABLE_FREETYPE). + unsigned int FontBuilderFlags; // Shared flags (for all fonts) for custom font builder. THIS IS BUILD IMPLEMENTATION DEPENDENT. Per-font override is also available in ImFontConfig. + // [Internal] Packing data int PackIdMouseCursors; // Custom texture rectangle ID for white pixel and mouse cursors int PackIdLines; // Custom texture rectangle ID for baked anti-aliased lines -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - typedef ImFontAtlasCustomRect CustomRect; // OBSOLETED in 1.72+ - typedef ImFontGlyphRangesBuilder GlyphRangesBuilder; // OBSOLETED in 1.67+ -#endif + // [Obsolete] + //typedef ImFontAtlasCustomRect CustomRect; // OBSOLETED in 1.72+ + //typedef ImFontGlyphRangesBuilder GlyphRangesBuilder; // OBSOLETED in 1.67+ }; // Font runtime data and rendering @@ -2676,8 +2855,9 @@ struct ImFont ImFontAtlas* ContainerAtlas; // 4-8 // out // // What we has been loaded into const ImFontConfig* ConfigData; // 4-8 // in // // Pointer within ContainerAtlas->ConfigData short ConfigDataCount; // 2 // in // ~ 1 // Number of ImFontConfig involved in creating this font. Bigger than 1 when merging multiple font sources into one ImFont. - ImWchar FallbackChar; // 2 // in // = '?' // Replacement character if a glyph isn't found. Only set via SetFallbackChar() - ImWchar EllipsisChar; // 2 // out // = -1 // Character used for ellipsis rendering. + ImWchar FallbackChar; // 2 // out // = FFFD/'?' // Character used if a glyph isn't found. + ImWchar EllipsisChar; // 2 // out // = '...' // Character used for ellipsis rendering. + ImWchar DotChar; // 2 // out // = '.' // Character used for ellipsis rendering (if a single '...' character isn't found) bool DirtyLookupTables; // 1 // out // float Scale; // 4 // in // = 1.f // Base font scale, multiplied by the per-window font scale which you can adjust with SetWindowFontScale() float Ascent, Descent; // 4+4 // out // // Ascent: distance from top to bottom of e.g. 'A' [0..FontSize] @@ -2707,16 +2887,153 @@ struct ImFont IMGUI_API void AddGlyph(const ImFontConfig* src_cfg, ImWchar c, float x0, float y0, float x1, float y1, float u0, float v0, float u1, float v1, float advance_x); IMGUI_API void AddRemapChar(ImWchar dst, ImWchar src, bool overwrite_dst = true); // Makes 'dst' character/glyph points to 'src' character/glyph. Currently needs to be called AFTER fonts have been built. IMGUI_API void SetGlyphVisible(ImWchar c, bool visible); - IMGUI_API void SetFallbackChar(ImWchar c); IMGUI_API bool IsGlyphRangeUnused(unsigned int c_begin, unsigned int c_last); }; +//----------------------------------------------------------------------------- +// [SECTION] Viewports +//----------------------------------------------------------------------------- + +// Flags stored in ImGuiViewport::Flags, giving indications to the platform backends. +enum ImGuiViewportFlags_ +{ + ImGuiViewportFlags_None = 0, + ImGuiViewportFlags_IsPlatformWindow = 1 << 0, // Represent a Platform Window + ImGuiViewportFlags_IsPlatformMonitor = 1 << 1, // Represent a Platform Monitor (unused yet) + ImGuiViewportFlags_OwnedByApp = 1 << 2 // Platform Window: is created/managed by the application (rather than a dear imgui backend) +}; + +// - Currently represents the Platform Window created by the application which is hosting our Dear ImGui windows. +// - In 'docking' branch with multi-viewport enabled, we extend this concept to have multiple active viewports. +// - In the future we will extend this concept further to also represent Platform Monitor and support a "no main platform window" operation mode. +// - About Main Area vs Work Area: +// - Main Area = entire viewport. +// - Work Area = entire viewport minus sections used by main menu bars (for platform windows), or by task bar (for platform monitor). +// - Windows are generally trying to stay within the Work Area of their host viewport. +struct ImGuiViewport +{ + ImGuiViewportFlags Flags; // See ImGuiViewportFlags_ + ImVec2 Pos; // Main Area: Position of the viewport (Dear ImGui coordinates are the same as OS desktop/native coordinates) + ImVec2 Size; // Main Area: Size of the viewport. + ImVec2 WorkPos; // Work Area: Position of the viewport minus task bars, menus bars, status bars (>= Pos) + ImVec2 WorkSize; // Work Area: Size of the viewport minus task bars, menu bars, status bars (<= Size) + + // Platform/Backend Dependent Data + void* PlatformHandleRaw; // void* to hold lower-level, platform-native window handle (under Win32 this is expected to be a HWND, unused for other platforms) + + ImGuiViewport() { memset(this, 0, sizeof(*this)); } + + // Helpers + ImVec2 GetCenter() const { return ImVec2(Pos.x + Size.x * 0.5f, Pos.y + Size.y * 0.5f); } + ImVec2 GetWorkCenter() const { return ImVec2(WorkPos.x + WorkSize.x * 0.5f, WorkPos.y + WorkSize.y * 0.5f); } +}; + +//----------------------------------------------------------------------------- +// [SECTION] Platform Dependent Interfaces +//----------------------------------------------------------------------------- + +// (Optional) Support for IME (Input Method Editor) via the io.SetPlatformImeDataFn() function. +struct ImGuiPlatformImeData +{ + bool WantVisible; // A widget wants the IME to be visible + ImVec2 InputPos; // Position of the input cursor + float InputLineHeight; // Line height + + ImGuiPlatformImeData() { memset(this, 0, sizeof(*this)); } +}; + +//----------------------------------------------------------------------------- +// [SECTION] Obsolete functions and types +// (Will be removed! Read 'API BREAKING CHANGES' section in imgui.cpp for details) +// Please keep your copy of dear imgui up to date! Occasionally set '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' in imconfig.h to stay ahead. +//----------------------------------------------------------------------------- + +namespace ImGui +{ +#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO + IMGUI_API int GetKeyIndex(ImGuiKey key); // map ImGuiKey_* values into legacy native key index. == io.KeyMap[key] +#else + static inline int GetKeyIndex(ImGuiKey key) { IM_ASSERT(key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END && "ImGuiKey and native_index was merged together and native_index is disabled by IMGUI_DISABLE_OBSOLETE_KEYIO. Please switch to ImGuiKey."); return key; } +#endif +} + +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS +namespace ImGui +{ + // OBSOLETED in 1.86 (from November 2021) + IMGUI_API void CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end); // Calculate coarse clipping for large list of evenly sized items. Prefer using ImGuiListClipper. + // OBSOLETED in 1.85 (from August 2021) + static inline float GetWindowContentRegionWidth() { return GetWindowContentRegionMax().x - GetWindowContentRegionMin().x; } + // OBSOLETED in 1.81 (from February 2021) + IMGUI_API bool ListBoxHeader(const char* label, int items_count, int height_in_items = -1); // Helper to calculate size from items_count and height_in_items + static inline bool ListBoxHeader(const char* label, const ImVec2& size = ImVec2(0, 0)) { return BeginListBox(label, size); } + static inline void ListBoxFooter() { EndListBox(); } + // OBSOLETED in 1.79 (from August 2020) + static inline void OpenPopupContextItem(const char* str_id = NULL, ImGuiMouseButton mb = 1) { OpenPopupOnItemClick(str_id, mb); } // Bool return value removed. Use IsWindowAppearing() in BeginPopup() instead. Renamed in 1.77, renamed back in 1.79. Sorry! + // OBSOLETED in 1.78 (from June 2020) + // Old drag/sliders functions that took a 'float power = 1.0' argument instead of flags. + // For shared code, you can version check at compile-time with `#if IMGUI_VERSION_NUM >= 17704`. + IMGUI_API bool DragScalar(const char* label, ImGuiDataType data_type, void* p_data, float v_speed, const void* p_min, const void* p_max, const char* format, float power); + IMGUI_API bool DragScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, float v_speed, const void* p_min, const void* p_max, const char* format, float power); + static inline bool DragFloat(const char* label, float* v, float v_speed, float v_min, float v_max, const char* format, float power) { return DragScalar(label, ImGuiDataType_Float, v, v_speed, &v_min, &v_max, format, power); } + static inline bool DragFloat2(const char* label, float v[2], float v_speed, float v_min, float v_max, const char* format, float power) { return DragScalarN(label, ImGuiDataType_Float, v, 2, v_speed, &v_min, &v_max, format, power); } + static inline bool DragFloat3(const char* label, float v[3], float v_speed, float v_min, float v_max, const char* format, float power) { return DragScalarN(label, ImGuiDataType_Float, v, 3, v_speed, &v_min, &v_max, format, power); } + static inline bool DragFloat4(const char* label, float v[4], float v_speed, float v_min, float v_max, const char* format, float power) { return DragScalarN(label, ImGuiDataType_Float, v, 4, v_speed, &v_min, &v_max, format, power); } + IMGUI_API bool SliderScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format, float power); + IMGUI_API bool SliderScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, const void* p_min, const void* p_max, const char* format, float power); + static inline bool SliderFloat(const char* label, float* v, float v_min, float v_max, const char* format, float power) { return SliderScalar(label, ImGuiDataType_Float, v, &v_min, &v_max, format, power); } + static inline bool SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* format, float power) { return SliderScalarN(label, ImGuiDataType_Float, v, 2, &v_min, &v_max, format, power); } + static inline bool SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* format, float power) { return SliderScalarN(label, ImGuiDataType_Float, v, 3, &v_min, &v_max, format, power); } + static inline bool SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* format, float power) { return SliderScalarN(label, ImGuiDataType_Float, v, 4, &v_min, &v_max, format, power); } + // OBSOLETED in 1.77 (from June 2020) + static inline bool BeginPopupContextWindow(const char* str_id, ImGuiMouseButton mb, bool over_items) { return BeginPopupContextWindow(str_id, mb | (over_items ? 0 : ImGuiPopupFlags_NoOpenOverItems)); } + + // Some of the older obsolete names along with their replacement (commented out so they are not reported in IDE) + //static inline void TreeAdvanceToLabelPos() { SetCursorPosX(GetCursorPosX() + GetTreeNodeToLabelSpacing()); } // OBSOLETED in 1.72 (from July 2019) + //static inline void SetNextTreeNodeOpen(bool open, ImGuiCond cond = 0) { SetNextItemOpen(open, cond); } // OBSOLETED in 1.71 (from June 2019) + //static inline float GetContentRegionAvailWidth() { return GetContentRegionAvail().x; } // OBSOLETED in 1.70 (from May 2019) + //static inline ImDrawList* GetOverlayDrawList() { return GetForegroundDrawList(); } // OBSOLETED in 1.69 (from Mar 2019) + //static inline void SetScrollHere(float ratio = 0.5f) { SetScrollHereY(ratio); } // OBSOLETED in 1.66 (from Nov 2018) + //static inline bool IsItemDeactivatedAfterChange() { return IsItemDeactivatedAfterEdit(); } // OBSOLETED in 1.63 (from Aug 2018) + //static inline bool IsAnyWindowFocused() { return IsWindowFocused(ImGuiFocusedFlags_AnyWindow); } // OBSOLETED in 1.60 (from Apr 2018) + //static inline bool IsAnyWindowHovered() { return IsWindowHovered(ImGuiHoveredFlags_AnyWindow); } // OBSOLETED in 1.60 (between Dec 2017 and Apr 2018) + //static inline void ShowTestWindow() { return ShowDemoWindow(); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) + //static inline bool IsRootWindowFocused() { return IsWindowFocused(ImGuiFocusedFlags_RootWindow); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) + //static inline bool IsRootWindowOrAnyChildFocused() { return IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) + //static inline void SetNextWindowContentWidth(float w) { SetNextWindowContentSize(ImVec2(w, 0.0f)); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) + //static inline float GetItemsLineHeightWithSpacing() { return GetFrameHeightWithSpacing(); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) +} + +// OBSOLETED in 1.82 (from Mars 2021): flags for AddRect(), AddRectFilled(), AddImageRounded(), PathRect() +typedef ImDrawFlags ImDrawCornerFlags; +enum ImDrawCornerFlags_ +{ + ImDrawCornerFlags_None = ImDrawFlags_RoundCornersNone, // Was == 0 prior to 1.82, this is now == ImDrawFlags_RoundCornersNone which is != 0 and not implicit + ImDrawCornerFlags_TopLeft = ImDrawFlags_RoundCornersTopLeft, // Was == 0x01 (1 << 0) prior to 1.82. Order matches ImDrawFlags_NoRoundCorner* flag (we exploit this internally). + ImDrawCornerFlags_TopRight = ImDrawFlags_RoundCornersTopRight, // Was == 0x02 (1 << 1) prior to 1.82. + ImDrawCornerFlags_BotLeft = ImDrawFlags_RoundCornersBottomLeft, // Was == 0x04 (1 << 2) prior to 1.82. + ImDrawCornerFlags_BotRight = ImDrawFlags_RoundCornersBottomRight, // Was == 0x08 (1 << 3) prior to 1.82. + ImDrawCornerFlags_All = ImDrawFlags_RoundCornersAll, // Was == 0x0F prior to 1.82 + ImDrawCornerFlags_Top = ImDrawCornerFlags_TopLeft | ImDrawCornerFlags_TopRight, + ImDrawCornerFlags_Bot = ImDrawCornerFlags_BotLeft | ImDrawCornerFlags_BotRight, + ImDrawCornerFlags_Left = ImDrawCornerFlags_TopLeft | ImDrawCornerFlags_BotLeft, + ImDrawCornerFlags_Right = ImDrawCornerFlags_TopRight | ImDrawCornerFlags_BotRight +}; + +#endif // #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + +//----------------------------------------------------------------------------- + #if defined(__clang__) #pragma clang diagnostic pop #elif defined(__GNUC__) #pragma GCC diagnostic pop #endif +#ifdef _MSC_VER +#pragma warning (pop) +#endif + // Include imgui_user.h at the end of imgui.h (convenient for user to only explicitly include vanilla imgui.h) #ifdef IMGUI_INCLUDE_IMGUI_USER_H #include "imgui_user.h" diff --git a/apps/common/external/imgui/imgui_draw.cpp b/apps/common/external/imgui/imgui_draw.cpp index 8a67566490..a99e6b24f7 100644 --- a/apps/common/external/imgui/imgui_draw.cpp +++ b/apps/common/external/imgui/imgui_draw.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.80 +// dear imgui, v1.87 // (drawing and font code) /* @@ -32,7 +32,11 @@ Index of this file: #ifndef IMGUI_DEFINE_MATH_OPERATORS #define IMGUI_DEFINE_MATH_OPERATORS #endif + #include "imgui_internal.h" +#ifdef IMGUI_ENABLE_FREETYPE +#include "misc/freetype/imgui_freetype.h" +#endif #include // vsnprintf, sscanf, printf #if !defined(alloca) @@ -50,9 +54,12 @@ Index of this file: // Visual Studio warnings #ifdef _MSC_VER -#pragma warning (disable: 4127) // condition expression is constant -#pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff) -#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen +#pragma warning (disable: 4127) // condition expression is constant +#pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff) +#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen +#pragma warning (disable: 6255) // [Static Analyzer] _alloca indicates failure by raising a stack overflow exception. Consider using _malloca instead. +#pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2). +#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). [MSVC Static Analyzer) #endif // Clang/GCC warnings with -Weverything @@ -101,6 +108,9 @@ namespace IMGUI_STB_NAMESPACE #ifdef _MSC_VER #pragma warning (push) #pragma warning (disable: 4456) // declaration of 'xx' hides previous local declaration +#pragma warning (disable: 6011) // (stb_rectpack) Dereferencing NULL pointer 'cur->next'. +#pragma warning (disable: 6385) // (stb_truetype) Reading invalid data from 'buffer': the readable size is '_Old_3`kernel_width' bytes, but '3' bytes may be read. +#pragma warning (disable: 28182) // (stb_rectpack) Dereferencing NULL pointer. 'cur' contains the same NULL value as 'cur->next' did. #endif #if defined(__clang__) @@ -118,7 +128,7 @@ namespace IMGUI_STB_NAMESPACE #endif #ifndef STB_RECT_PACK_IMPLEMENTATION // in case the user already have an implementation in the _same_ compilation unit (e.g. unity builds) -#ifndef IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION +#ifndef IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION // in case the user already have an implementation in another compilation unit #define STBRP_STATIC #define STBRP_ASSERT(x) do { IM_ASSERT(x); } while (0) #define STBRP_SORT ImQsort @@ -131,8 +141,9 @@ namespace IMGUI_STB_NAMESPACE #endif #endif +#ifdef IMGUI_ENABLE_STB_TRUETYPE #ifndef STB_TRUETYPE_IMPLEMENTATION // in case the user already have an implementation in the _same_ compilation unit (e.g. unity builds) -#ifndef IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION +#ifndef IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION // in case the user already have an implementation in another compilation unit #define STBTT_malloc(x,u) ((void)(u), IM_ALLOC(x)) #define STBTT_free(x,u) ((void)(u), IM_FREE(x)) #define STBTT_assert(x) do { IM_ASSERT(x); } while(0) @@ -140,7 +151,7 @@ namespace IMGUI_STB_NAMESPACE #define STBTT_sqrt(x) ImSqrt(x) #define STBTT_pow(x,y) ImPow(x,y) #define STBTT_fabs(x) ImFabs(x) -#define STBTT_ifloor(x) ((int)ImFloorStd(x)) +#define STBTT_ifloor(x) ((int)ImFloorSigned(x)) #define STBTT_iceil(x) ((int)ImCeil(x)) #define STBTT_STATIC #define STB_TRUETYPE_IMPLEMENTATION @@ -153,6 +164,7 @@ namespace IMGUI_STB_NAMESPACE #include "imstb_truetype.h" #endif #endif +#endif // IMGUI_ENABLE_STB_TRUETYPE #if defined(__GNUC__) #pragma GCC diagnostic pop @@ -368,29 +380,31 @@ ImDrawListSharedData::ImDrawListSharedData() const float a = ((float)i * 2 * IM_PI) / (float)IM_ARRAYSIZE(ArcFastVtx); ArcFastVtx[i] = ImVec2(ImCos(a), ImSin(a)); } + ArcFastRadiusCutoff = IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_R(IM_DRAWLIST_ARCFAST_SAMPLE_MAX, CircleSegmentMaxError); } -void ImDrawListSharedData::SetCircleSegmentMaxError(float max_error) +void ImDrawListSharedData::SetCircleTessellationMaxError(float max_error) { if (CircleSegmentMaxError == max_error) return; + + IM_ASSERT(max_error > 0.0f); CircleSegmentMaxError = max_error; for (int i = 0; i < IM_ARRAYSIZE(CircleSegmentCounts); i++) { - const float radius = i + 1.0f; - const int segment_count = IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(radius, CircleSegmentMaxError); - CircleSegmentCounts[i] = (ImU8)ImMin(segment_count, 255); + const float radius = (float)i; + CircleSegmentCounts[i] = (ImU8)((i > 0) ? IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(radius, CircleSegmentMaxError) : 0); } + ArcFastRadiusCutoff = IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_R(IM_DRAWLIST_ARCFAST_SAMPLE_MAX, CircleSegmentMaxError); } // Initialize before use in a new frame. We always have a command ready in the buffer. void ImDrawList::_ResetForNewFrame() { // Verify that the ImDrawCmd fields we want to memcmp() are contiguous in memory. - // (those should be IM_STATIC_ASSERT() in theory but with our pre C++11 setup the whole check doesn't compile with GCC) - IM_ASSERT(IM_OFFSETOF(ImDrawCmd, ClipRect) == 0); - IM_ASSERT(IM_OFFSETOF(ImDrawCmd, TextureId) == sizeof(ImVec4)); - IM_ASSERT(IM_OFFSETOF(ImDrawCmd, VtxOffset) == sizeof(ImVec4) + sizeof(ImTextureID)); + IM_STATIC_ASSERT(IM_OFFSETOF(ImDrawCmd, ClipRect) == 0); + IM_STATIC_ASSERT(IM_OFFSETOF(ImDrawCmd, TextureId) == sizeof(ImVec4)); + IM_STATIC_ASSERT(IM_OFFSETOF(ImDrawCmd, VtxOffset) == sizeof(ImVec4) + sizeof(ImTextureID)); CmdBuffer.resize(0); IdxBuffer.resize(0); @@ -458,6 +472,7 @@ void ImDrawList::_PopUnusedDrawCmd() void ImDrawList::AddCallback(ImDrawCallback callback, void* callback_data) { + IM_ASSERT_PARANOID(CmdBuffer.Size > 0); ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; IM_ASSERT(curr_cmd->UserCallback == NULL); if (curr_cmd->ElemCount != 0) @@ -472,15 +487,30 @@ void ImDrawList::AddCallback(ImDrawCallback callback, void* callback_data) } // Compare ClipRect, TextureId and VtxOffset with a single memcmp() -#define ImDrawCmd_HeaderSize (IM_OFFSETOF(ImDrawCmd, VtxOffset) + sizeof(unsigned int)) -#define ImDrawCmd_HeaderCompare(CMD_LHS, CMD_RHS) (memcmp(CMD_LHS, CMD_RHS, ImDrawCmd_HeaderSize)) // Compare ClipRect, TextureId, VtxOffset -#define ImDrawCmd_HeaderCopy(CMD_DST, CMD_SRC) (memcpy(CMD_DST, CMD_SRC, ImDrawCmd_HeaderSize)) // Copy ClipRect, TextureId, VtxOffset +#define ImDrawCmd_HeaderSize (IM_OFFSETOF(ImDrawCmd, VtxOffset) + sizeof(unsigned int)) +#define ImDrawCmd_HeaderCompare(CMD_LHS, CMD_RHS) (memcmp(CMD_LHS, CMD_RHS, ImDrawCmd_HeaderSize)) // Compare ClipRect, TextureId, VtxOffset +#define ImDrawCmd_HeaderCopy(CMD_DST, CMD_SRC) (memcpy(CMD_DST, CMD_SRC, ImDrawCmd_HeaderSize)) // Copy ClipRect, TextureId, VtxOffset +#define ImDrawCmd_AreSequentialIdxOffset(CMD_0, CMD_1) (CMD_0->IdxOffset + CMD_0->ElemCount == CMD_1->IdxOffset) + +// Try to merge two last draw commands +void ImDrawList::_TryMergeDrawCmds() +{ + IM_ASSERT_PARANOID(CmdBuffer.Size > 0); + ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; + ImDrawCmd* prev_cmd = curr_cmd - 1; + if (ImDrawCmd_HeaderCompare(curr_cmd, prev_cmd) == 0 && ImDrawCmd_AreSequentialIdxOffset(prev_cmd, curr_cmd) && curr_cmd->UserCallback == NULL && prev_cmd->UserCallback == NULL) + { + prev_cmd->ElemCount += curr_cmd->ElemCount; + CmdBuffer.pop_back(); + } +} // Our scheme may appears a bit unusual, basically we want the most-common calls AddLine AddRect etc. to not have to perform any check so we always have a command ready in the stack. // The cost of figuring out if a new command has to be added or if we can merge is paid in those Update** functions only. void ImDrawList::_OnChangedClipRect() { // If current command is used with different settings we need to add a new command + IM_ASSERT_PARANOID(CmdBuffer.Size > 0); ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; if (curr_cmd->ElemCount != 0 && memcmp(&curr_cmd->ClipRect, &_CmdHeader.ClipRect, sizeof(ImVec4)) != 0) { @@ -491,7 +521,7 @@ void ImDrawList::_OnChangedClipRect() // Try to merge with previous command if it matches, else use current command ImDrawCmd* prev_cmd = curr_cmd - 1; - if (curr_cmd->ElemCount == 0 && CmdBuffer.Size > 1 && ImDrawCmd_HeaderCompare(&_CmdHeader, prev_cmd) == 0 && prev_cmd->UserCallback == NULL) + if (curr_cmd->ElemCount == 0 && CmdBuffer.Size > 1 && ImDrawCmd_HeaderCompare(&_CmdHeader, prev_cmd) == 0 && ImDrawCmd_AreSequentialIdxOffset(prev_cmd, curr_cmd) && prev_cmd->UserCallback == NULL) { CmdBuffer.pop_back(); return; @@ -503,6 +533,7 @@ void ImDrawList::_OnChangedClipRect() void ImDrawList::_OnChangedTextureID() { // If current command is used with different settings we need to add a new command + IM_ASSERT_PARANOID(CmdBuffer.Size > 0); ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; if (curr_cmd->ElemCount != 0 && curr_cmd->TextureId != _CmdHeader.TextureId) { @@ -513,7 +544,7 @@ void ImDrawList::_OnChangedTextureID() // Try to merge with previous command if it matches, else use current command ImDrawCmd* prev_cmd = curr_cmd - 1; - if (curr_cmd->ElemCount == 0 && CmdBuffer.Size > 1 && ImDrawCmd_HeaderCompare(&_CmdHeader, prev_cmd) == 0 && prev_cmd->UserCallback == NULL) + if (curr_cmd->ElemCount == 0 && CmdBuffer.Size > 1 && ImDrawCmd_HeaderCompare(&_CmdHeader, prev_cmd) == 0 && ImDrawCmd_AreSequentialIdxOffset(prev_cmd, curr_cmd) && prev_cmd->UserCallback == NULL) { CmdBuffer.pop_back(); return; @@ -526,6 +557,7 @@ void ImDrawList::_OnChangedVtxOffset() { // We don't need to compare curr_cmd->VtxOffset != _CmdHeader.VtxOffset because we know it'll be different at the time we call this. _VtxCurrentIdx = 0; + IM_ASSERT_PARANOID(CmdBuffer.Size > 0); ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; //IM_ASSERT(curr_cmd->VtxOffset != _CmdHeader.VtxOffset); // See #3349 if (curr_cmd->ElemCount != 0) @@ -537,6 +569,16 @@ void ImDrawList::_OnChangedVtxOffset() curr_cmd->VtxOffset = _CmdHeader.VtxOffset; } +int ImDrawList::_CalcCircleAutoSegmentCount(float radius) const +{ + // Automatic segment count + const int radius_idx = (int)(radius + 0.999999f); // ceil to never reduce accuracy + if (radius_idx < IM_ARRAYSIZE(_Data->CircleSegmentCounts)) + return _Data->CircleSegmentCounts[radius_idx]; // Use cached value + else + return IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(radius, _Data->CircleSegmentMaxError); +} + // Render-level scissoring. This is passed down to your render function but not used for CPU-side coarse clipping. Prefer using higher-level ImGui::PushClipRect() to affect logic (hit-testing and widget culling) void ImDrawList::PushClipRect(ImVec2 cr_min, ImVec2 cr_max, bool intersect_with_current_clip_rect) { @@ -668,17 +710,20 @@ void ImDrawList::PrimQuadUV(const ImVec2& a, const ImVec2& b, const ImVec2& c, c } // On AddPolyline() and AddConvexPolyFilled() we intentionally avoid using ImVec2 and superfluous function calls to optimize debug/non-inlined builds. -// Those macros expects l-values. -#define IM_NORMALIZE2F_OVER_ZERO(VX,VY) do { float d2 = VX*VX + VY*VY; if (d2 > 0.0f) { float inv_len = 1.0f / ImSqrt(d2); VX *= inv_len; VY *= inv_len; } } while (0) -#define IM_FIXNORMAL2F(VX,VY) do { float d2 = VX*VX + VY*VY; if (d2 < 0.5f) d2 = 0.5f; float inv_lensq = 1.0f / d2; VX *= inv_lensq; VY *= inv_lensq; } while (0) +// - Those macros expects l-values and need to be used as their own statement. +// - Those macros are intentionally not surrounded by the 'do {} while (0)' idiom because even that translates to runtime with debug compilers. +#define IM_NORMALIZE2F_OVER_ZERO(VX,VY) { float d2 = VX*VX + VY*VY; if (d2 > 0.0f) { float inv_len = ImRsqrt(d2); VX *= inv_len; VY *= inv_len; } } (void)0 +#define IM_FIXNORMAL2F_MAX_INVLEN2 100.0f // 500.0f (see #4053, #3366) +#define IM_FIXNORMAL2F(VX,VY) { float d2 = VX*VX + VY*VY; if (d2 > 0.000001f) { float inv_len2 = 1.0f / d2; if (inv_len2 > IM_FIXNORMAL2F_MAX_INVLEN2) inv_len2 = IM_FIXNORMAL2F_MAX_INVLEN2; VX *= inv_len2; VY *= inv_len2; } } (void)0 // TODO: Thickness anti-aliased lines cap are missing their AA fringe. // We avoid using the ImVec2 math operators here to reduce cost to a minimum for debug/non-inlined builds. -void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 col, bool closed, float thickness) +void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 col, ImDrawFlags flags, float thickness) { if (points_count < 2) return; + const bool closed = (flags & ImDrawFlags_Closed) != 0; const ImVec2 opaque_uv = _Data->TexUvWhitePixel; const int count = closed ? points_count : points_count - 1; // The number of line segments we need to draw const bool thick_line = (thickness > _FringeScale); @@ -1010,32 +1055,101 @@ void ImDrawList::AddConvexPolyFilled(const ImVec2* points, const int points_coun } } -void ImDrawList::PathArcToFast(const ImVec2& center, float radius, int a_min_of_12, int a_max_of_12) +void ImDrawList::_PathArcToFastEx(const ImVec2& center, float radius, int a_min_sample, int a_max_sample, int a_step) { - if (radius == 0.0f || a_min_of_12 > a_max_of_12) + if (radius <= 0.0f) { _Path.push_back(center); return; } - // For legacy reason the PathArcToFast() always takes angles where 2*PI is represented by 12, - // but it is possible to set IM_DRAWLIST_ARCFAST_TESSELATION_MULTIPLIER to a higher value. This should compile to a no-op otherwise. -#if IM_DRAWLIST_ARCFAST_TESSELLATION_MULTIPLIER != 1 - a_min_of_12 *= IM_DRAWLIST_ARCFAST_TESSELLATION_MULTIPLIER; - a_max_of_12 *= IM_DRAWLIST_ARCFAST_TESSELLATION_MULTIPLIER; -#endif + // Calculate arc auto segment step size + if (a_step <= 0) + a_step = IM_DRAWLIST_ARCFAST_SAMPLE_MAX / _CalcCircleAutoSegmentCount(radius); + + // Make sure we never do steps larger than one quarter of the circle + a_step = ImClamp(a_step, 1, IM_DRAWLIST_ARCFAST_TABLE_SIZE / 4); + + const int sample_range = ImAbs(a_max_sample - a_min_sample); + const int a_next_step = a_step; + + int samples = sample_range + 1; + bool extra_max_sample = false; + if (a_step > 1) + { + samples = sample_range / a_step + 1; + const int overstep = sample_range % a_step; - _Path.reserve(_Path.Size + (a_max_of_12 - a_min_of_12 + 1)); - for (int a = a_min_of_12; a <= a_max_of_12; a++) + if (overstep > 0) + { + extra_max_sample = true; + samples++; + + // When we have overstep to avoid awkwardly looking one long line and one tiny one at the end, + // distribute first step range evenly between them by reducing first step size. + if (sample_range > 0) + a_step -= (a_step - overstep) / 2; + } + } + + _Path.resize(_Path.Size + samples); + ImVec2* out_ptr = _Path.Data + (_Path.Size - samples); + + int sample_index = a_min_sample; + if (sample_index < 0 || sample_index >= IM_DRAWLIST_ARCFAST_SAMPLE_MAX) { - const ImVec2& c = _Data->ArcFastVtx[a % IM_ARRAYSIZE(_Data->ArcFastVtx)]; - _Path.push_back(ImVec2(center.x + c.x * radius, center.y + c.y * radius)); + sample_index = sample_index % IM_DRAWLIST_ARCFAST_SAMPLE_MAX; + if (sample_index < 0) + sample_index += IM_DRAWLIST_ARCFAST_SAMPLE_MAX; + } + + if (a_max_sample >= a_min_sample) + { + for (int a = a_min_sample; a <= a_max_sample; a += a_step, sample_index += a_step, a_step = a_next_step) + { + // a_step is clamped to IM_DRAWLIST_ARCFAST_SAMPLE_MAX, so we have guaranteed that it will not wrap over range twice or more + if (sample_index >= IM_DRAWLIST_ARCFAST_SAMPLE_MAX) + sample_index -= IM_DRAWLIST_ARCFAST_SAMPLE_MAX; + + const ImVec2 s = _Data->ArcFastVtx[sample_index]; + out_ptr->x = center.x + s.x * radius; + out_ptr->y = center.y + s.y * radius; + out_ptr++; + } } + else + { + for (int a = a_min_sample; a >= a_max_sample; a -= a_step, sample_index -= a_step, a_step = a_next_step) + { + // a_step is clamped to IM_DRAWLIST_ARCFAST_SAMPLE_MAX, so we have guaranteed that it will not wrap over range twice or more + if (sample_index < 0) + sample_index += IM_DRAWLIST_ARCFAST_SAMPLE_MAX; + + const ImVec2 s = _Data->ArcFastVtx[sample_index]; + out_ptr->x = center.x + s.x * radius; + out_ptr->y = center.y + s.y * radius; + out_ptr++; + } + } + + if (extra_max_sample) + { + int normalized_max_sample = a_max_sample % IM_DRAWLIST_ARCFAST_SAMPLE_MAX; + if (normalized_max_sample < 0) + normalized_max_sample += IM_DRAWLIST_ARCFAST_SAMPLE_MAX; + + const ImVec2 s = _Data->ArcFastVtx[normalized_max_sample]; + out_ptr->x = center.x + s.x * radius; + out_ptr->y = center.y + s.y * radius; + out_ptr++; + } + + IM_ASSERT_PARANOID(_Path.Data + _Path.Size == out_ptr); } -void ImDrawList::PathArcTo(const ImVec2& center, float radius, float a_min, float a_max, int num_segments) +void ImDrawList::_PathArcToN(const ImVec2& center, float radius, float a_min, float a_max, int num_segments) { - if (radius == 0.0f) + if (radius <= 0.0f) { _Path.push_back(center); return; @@ -1051,6 +1165,67 @@ void ImDrawList::PathArcTo(const ImVec2& center, float radius, float a_min, floa } } +// 0: East, 3: South, 6: West, 9: North, 12: East +void ImDrawList::PathArcToFast(const ImVec2& center, float radius, int a_min_of_12, int a_max_of_12) +{ + if (radius <= 0.0f) + { + _Path.push_back(center); + return; + } + _PathArcToFastEx(center, radius, a_min_of_12 * IM_DRAWLIST_ARCFAST_SAMPLE_MAX / 12, a_max_of_12 * IM_DRAWLIST_ARCFAST_SAMPLE_MAX / 12, 0); +} + +void ImDrawList::PathArcTo(const ImVec2& center, float radius, float a_min, float a_max, int num_segments) +{ + if (radius <= 0.0f) + { + _Path.push_back(center); + return; + } + + if (num_segments > 0) + { + _PathArcToN(center, radius, a_min, a_max, num_segments); + return; + } + + // Automatic segment count + if (radius <= _Data->ArcFastRadiusCutoff) + { + const bool a_is_reverse = a_max < a_min; + + // We are going to use precomputed values for mid samples. + // Determine first and last sample in lookup table that belong to the arc. + const float a_min_sample_f = IM_DRAWLIST_ARCFAST_SAMPLE_MAX * a_min / (IM_PI * 2.0f); + const float a_max_sample_f = IM_DRAWLIST_ARCFAST_SAMPLE_MAX * a_max / (IM_PI * 2.0f); + + const int a_min_sample = a_is_reverse ? (int)ImFloorSigned(a_min_sample_f) : (int)ImCeil(a_min_sample_f); + const int a_max_sample = a_is_reverse ? (int)ImCeil(a_max_sample_f) : (int)ImFloorSigned(a_max_sample_f); + const int a_mid_samples = a_is_reverse ? ImMax(a_min_sample - a_max_sample, 0) : ImMax(a_max_sample - a_min_sample, 0); + + const float a_min_segment_angle = a_min_sample * IM_PI * 2.0f / IM_DRAWLIST_ARCFAST_SAMPLE_MAX; + const float a_max_segment_angle = a_max_sample * IM_PI * 2.0f / IM_DRAWLIST_ARCFAST_SAMPLE_MAX; + const bool a_emit_start = (a_min_segment_angle - a_min) != 0.0f; + const bool a_emit_end = (a_max - a_max_segment_angle) != 0.0f; + + _Path.reserve(_Path.Size + (a_mid_samples + 1 + (a_emit_start ? 1 : 0) + (a_emit_end ? 1 : 0))); + if (a_emit_start) + _Path.push_back(ImVec2(center.x + ImCos(a_min) * radius, center.y + ImSin(a_min) * radius)); + if (a_mid_samples > 0) + _PathArcToFastEx(center, radius, a_min_sample, a_max_sample, 0); + if (a_emit_end) + _Path.push_back(ImVec2(center.x + ImCos(a_max) * radius, center.y + ImSin(a_max) * radius)); + } + else + { + const float arc_length = ImAbs(a_max - a_min); + const int circle_segment_count = _CalcCircleAutoSegmentCount(radius); + const int arc_segment_count = ImMax((int)ImCeil(circle_segment_count * arc_length / (IM_PI * 2.0f)), (int)(2.0f * IM_PI / arc_length)); + _PathArcToN(center, radius, a_min, a_max, arc_segment_count); + } +} + ImVec2 ImBezierCubicCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, float t) { float u = 1.0f - t; @@ -1144,12 +1319,47 @@ void ImDrawList::PathBezierQuadraticCurveTo(const ImVec2& p2, const ImVec2& p3, } } -void ImDrawList::PathRect(const ImVec2& a, const ImVec2& b, float rounding, ImDrawCornerFlags rounding_corners) +IM_STATIC_ASSERT(ImDrawFlags_RoundCornersTopLeft == (1 << 4)); +static inline ImDrawFlags FixRectCornerFlags(ImDrawFlags flags) +{ +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + // Legacy Support for hard coded ~0 (used to be a suggested equivalent to ImDrawCornerFlags_All) + // ~0 --> ImDrawFlags_RoundCornersAll or 0 + if (flags == ~0) + return ImDrawFlags_RoundCornersAll; + + // Legacy Support for hard coded 0x01 to 0x0F (matching 15 out of 16 old flags combinations) + // 0x01 --> ImDrawFlags_RoundCornersTopLeft (VALUE 0x01 OVERLAPS ImDrawFlags_Closed but ImDrawFlags_Closed is never valid in this path!) + // 0x02 --> ImDrawFlags_RoundCornersTopRight + // 0x03 --> ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersTopRight + // 0x04 --> ImDrawFlags_RoundCornersBotLeft + // 0x05 --> ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersBotLeft + // ... + // 0x0F --> ImDrawFlags_RoundCornersAll or 0 + // (See all values in ImDrawCornerFlags_) + if (flags >= 0x01 && flags <= 0x0F) + return (flags << 4); + + // We cannot support hard coded 0x00 with 'float rounding > 0.0f' --> replace with ImDrawFlags_RoundCornersNone or use 'float rounding = 0.0f' +#endif + + // If this triggers, please update your code replacing hardcoded values with new ImDrawFlags_RoundCorners* values. + // Note that ImDrawFlags_Closed (== 0x01) is an invalid flag for AddRect(), AddRectFilled(), PathRect() etc... + IM_ASSERT((flags & 0x0F) == 0 && "Misuse of legacy hardcoded ImDrawCornerFlags values!"); + + if ((flags & ImDrawFlags_RoundCornersMask_) == 0) + flags |= ImDrawFlags_RoundCornersAll; + + return flags; +} + +void ImDrawList::PathRect(const ImVec2& a, const ImVec2& b, float rounding, ImDrawFlags flags) { - rounding = ImMin(rounding, ImFabs(b.x - a.x) * ( ((rounding_corners & ImDrawCornerFlags_Top) == ImDrawCornerFlags_Top) || ((rounding_corners & ImDrawCornerFlags_Bot) == ImDrawCornerFlags_Bot) ? 0.5f : 1.0f ) - 1.0f); - rounding = ImMin(rounding, ImFabs(b.y - a.y) * ( ((rounding_corners & ImDrawCornerFlags_Left) == ImDrawCornerFlags_Left) || ((rounding_corners & ImDrawCornerFlags_Right) == ImDrawCornerFlags_Right) ? 0.5f : 1.0f ) - 1.0f); + flags = FixRectCornerFlags(flags); + rounding = ImMin(rounding, ImFabs(b.x - a.x) * ( ((flags & ImDrawFlags_RoundCornersTop) == ImDrawFlags_RoundCornersTop) || ((flags & ImDrawFlags_RoundCornersBottom) == ImDrawFlags_RoundCornersBottom) ? 0.5f : 1.0f ) - 1.0f); + rounding = ImMin(rounding, ImFabs(b.y - a.y) * ( ((flags & ImDrawFlags_RoundCornersLeft) == ImDrawFlags_RoundCornersLeft) || ((flags & ImDrawFlags_RoundCornersRight) == ImDrawFlags_RoundCornersRight) ? 0.5f : 1.0f ) - 1.0f); - if (rounding <= 0.0f || rounding_corners == 0) + if (rounding <= 0.0f || (flags & ImDrawFlags_RoundCornersMask_) == ImDrawFlags_RoundCornersNone) { PathLineTo(a); PathLineTo(ImVec2(b.x, a.y)); @@ -1158,10 +1368,10 @@ void ImDrawList::PathRect(const ImVec2& a, const ImVec2& b, float rounding, ImDr } else { - const float rounding_tl = (rounding_corners & ImDrawCornerFlags_TopLeft) ? rounding : 0.0f; - const float rounding_tr = (rounding_corners & ImDrawCornerFlags_TopRight) ? rounding : 0.0f; - const float rounding_br = (rounding_corners & ImDrawCornerFlags_BotRight) ? rounding : 0.0f; - const float rounding_bl = (rounding_corners & ImDrawCornerFlags_BotLeft) ? rounding : 0.0f; + const float rounding_tl = (flags & ImDrawFlags_RoundCornersTopLeft) ? rounding : 0.0f; + const float rounding_tr = (flags & ImDrawFlags_RoundCornersTopRight) ? rounding : 0.0f; + const float rounding_br = (flags & ImDrawFlags_RoundCornersBottomRight) ? rounding : 0.0f; + const float rounding_bl = (flags & ImDrawFlags_RoundCornersBottomLeft) ? rounding : 0.0f; PathArcToFast(ImVec2(a.x + rounding_tl, a.y + rounding_tl), rounding_tl, 6, 9); PathArcToFast(ImVec2(b.x - rounding_tr, a.y + rounding_tr), rounding_tr, 9, 12); PathArcToFast(ImVec2(b.x - rounding_br, b.y - rounding_br), rounding_br, 0, 3); @@ -1175,35 +1385,35 @@ void ImDrawList::AddLine(const ImVec2& p1, const ImVec2& p2, ImU32 col, float th return; PathLineTo(p1 + ImVec2(0.5f, 0.5f)); PathLineTo(p2 + ImVec2(0.5f, 0.5f)); - PathStroke(col, false, thickness); + PathStroke(col, 0, thickness); } // p_min = upper-left, p_max = lower-right // Note we don't render 1 pixels sized rectangles properly. -void ImDrawList::AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding, ImDrawCornerFlags rounding_corners, float thickness) +void ImDrawList::AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding, ImDrawFlags flags, float thickness) { if ((col & IM_COL32_A_MASK) == 0) return; if (Flags & ImDrawListFlags_AntiAliasedLines) - PathRect(p_min + ImVec2(0.50f, 0.50f), p_max - ImVec2(0.50f, 0.50f), rounding, rounding_corners); + PathRect(p_min + ImVec2(0.50f, 0.50f), p_max - ImVec2(0.50f, 0.50f), rounding, flags); else - PathRect(p_min + ImVec2(0.50f, 0.50f), p_max - ImVec2(0.49f, 0.49f), rounding, rounding_corners); // Better looking lower-right corner and rounded non-AA shapes. - PathStroke(col, true, thickness); + PathRect(p_min + ImVec2(0.50f, 0.50f), p_max - ImVec2(0.49f, 0.49f), rounding, flags); // Better looking lower-right corner and rounded non-AA shapes. + PathStroke(col, ImDrawFlags_Closed, thickness); } -void ImDrawList::AddRectFilled(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding, ImDrawCornerFlags rounding_corners) +void ImDrawList::AddRectFilled(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding, ImDrawFlags flags) { if ((col & IM_COL32_A_MASK) == 0) return; - if (rounding > 0.0f) + if (rounding <= 0.0f || (flags & ImDrawFlags_RoundCornersMask_) == ImDrawFlags_RoundCornersNone) { - PathRect(p_min, p_max, rounding, rounding_corners); - PathFillConvex(col); + PrimReserve(6, 4); + PrimRect(p_min, p_max, col); } else { - PrimReserve(6, 4); - PrimRect(p_min, p_max, col); + PathRect(p_min, p_max, rounding, flags); + PathFillConvex(col); } } @@ -1232,7 +1442,7 @@ void ImDrawList::AddQuad(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, c PathLineTo(p2); PathLineTo(p3); PathLineTo(p4); - PathStroke(col, true, thickness); + PathStroke(col, ImDrawFlags_Closed, thickness); } void ImDrawList::AddQuadFilled(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col) @@ -1255,7 +1465,7 @@ void ImDrawList::AddTriangle(const ImVec2& p1, const ImVec2& p2, const ImVec2& p PathLineTo(p1); PathLineTo(p2); PathLineTo(p3); - PathStroke(col, true, thickness); + PathStroke(col, ImDrawFlags_Closed, thickness); } void ImDrawList::AddTriangleFilled(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col) @@ -1274,29 +1484,23 @@ void ImDrawList::AddCircle(const ImVec2& center, float radius, ImU32 col, int nu if ((col & IM_COL32_A_MASK) == 0 || radius <= 0.0f) return; - // Obtain segment count if (num_segments <= 0) { - // Automatic segment count - const int radius_idx = (int)radius - 1; - if (radius_idx < IM_ARRAYSIZE(_Data->CircleSegmentCounts)) - num_segments = _Data->CircleSegmentCounts[radius_idx]; // Use cached value - else - num_segments = IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(radius, _Data->CircleSegmentMaxError); + // Use arc with automatic segment count + _PathArcToFastEx(center, radius - 0.5f, 0, IM_DRAWLIST_ARCFAST_SAMPLE_MAX, 0); + _Path.Size--; } else { // Explicit segment count (still clamp to avoid drawing insanely tessellated shapes) num_segments = ImClamp(num_segments, 3, IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX); - } - // Because we are filling a closed shape we remove 1 from the count of segments/points - const float a_max = (IM_PI * 2.0f) * ((float)num_segments - 1.0f) / (float)num_segments; - if (num_segments == 12) - PathArcToFast(center, radius - 0.5f, 0, 12 - 1); - else + // Because we are filling a closed shape we remove 1 from the count of segments/points + const float a_max = (IM_PI * 2.0f) * ((float)num_segments - 1.0f) / (float)num_segments; PathArcTo(center, radius - 0.5f, 0.0f, a_max, num_segments - 1); - PathStroke(col, true, thickness); + } + + PathStroke(col, ImDrawFlags_Closed, thickness); } void ImDrawList::AddCircleFilled(const ImVec2& center, float radius, ImU32 col, int num_segments) @@ -1304,28 +1508,22 @@ void ImDrawList::AddCircleFilled(const ImVec2& center, float radius, ImU32 col, if ((col & IM_COL32_A_MASK) == 0 || radius <= 0.0f) return; - // Obtain segment count if (num_segments <= 0) { - // Automatic segment count - const int radius_idx = (int)radius - 1; - if (radius_idx < IM_ARRAYSIZE(_Data->CircleSegmentCounts)) - num_segments = _Data->CircleSegmentCounts[radius_idx]; // Use cached value - else - num_segments = IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(radius, _Data->CircleSegmentMaxError); + // Use arc with automatic segment count + _PathArcToFastEx(center, radius, 0, IM_DRAWLIST_ARCFAST_SAMPLE_MAX, 0); + _Path.Size--; } else { // Explicit segment count (still clamp to avoid drawing insanely tessellated shapes) num_segments = ImClamp(num_segments, 3, IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX); - } - // Because we are filling a closed shape we remove 1 from the count of segments/points - const float a_max = (IM_PI * 2.0f) * ((float)num_segments - 1.0f) / (float)num_segments; - if (num_segments == 12) - PathArcToFast(center, radius, 0, 12 - 1); - else + // Because we are filling a closed shape we remove 1 from the count of segments/points + const float a_max = (IM_PI * 2.0f) * ((float)num_segments - 1.0f) / (float)num_segments; PathArcTo(center, radius, 0.0f, a_max, num_segments - 1); + } + PathFillConvex(col); } @@ -1338,7 +1536,7 @@ void ImDrawList::AddNgon(const ImVec2& center, float radius, ImU32 col, int num_ // Because we are filling a closed shape we remove 1 from the count of segments/points const float a_max = (IM_PI * 2.0f) * ((float)num_segments - 1.0f) / (float)num_segments; PathArcTo(center, radius - 0.5f, 0.0f, a_max, num_segments - 1); - PathStroke(col, true, thickness); + PathStroke(col, ImDrawFlags_Closed, thickness); } // Guaranteed to honor 'num_segments' @@ -1361,7 +1559,7 @@ void ImDrawList::AddBezierCubic(const ImVec2& p1, const ImVec2& p2, const ImVec2 PathLineTo(p1); PathBezierCubicCurveTo(p2, p3, p4, num_segments); - PathStroke(col, false, thickness); + PathStroke(col, 0, thickness); } // Quadratic Bezier takes 3 controls points @@ -1372,7 +1570,7 @@ void ImDrawList::AddBezierQuadratic(const ImVec2& p1, const ImVec2& p2, const Im PathLineTo(p1); PathBezierQuadraticCurveTo(p2, p3, num_segments); - PathStroke(col, false, thickness); + PathStroke(col, 0, thickness); } void ImDrawList::AddText(const ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end, float wrap_width, const ImVec4* cpu_fine_clip_rect) @@ -1441,23 +1639,24 @@ void ImDrawList::AddImageQuad(ImTextureID user_texture_id, const ImVec2& p1, con PopTextureID(); } -void ImDrawList::AddImageRounded(ImTextureID user_texture_id, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min, const ImVec2& uv_max, ImU32 col, float rounding, ImDrawCornerFlags rounding_corners) +void ImDrawList::AddImageRounded(ImTextureID user_texture_id, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min, const ImVec2& uv_max, ImU32 col, float rounding, ImDrawFlags flags) { if ((col & IM_COL32_A_MASK) == 0) return; - if (rounding <= 0.0f || (rounding_corners & ImDrawCornerFlags_All) == 0) + flags = FixRectCornerFlags(flags); + if (rounding <= 0.0f || (flags & ImDrawFlags_RoundCornersMask_) == ImDrawFlags_RoundCornersNone) { AddImage(user_texture_id, p_min, p_max, uv_min, uv_max, col); return; } - const bool push_texture_id = _TextureIdStack.empty() || user_texture_id != _TextureIdStack.back(); + const bool push_texture_id = user_texture_id != _CmdHeader.TextureId; if (push_texture_id) PushTextureID(user_texture_id); int vert_start_idx = VtxBuffer.Size; - PathRect(p_min, p_max, rounding, rounding_corners); + PathRect(p_min, p_max, rounding, flags); PathFillConvex(col); int vert_end_idx = VtxBuffer.Size; ImGui::ShadeVertsLinearUV(this, vert_start_idx, vert_end_idx, p_min, p_max, uv_min, uv_max, true); @@ -1534,13 +1733,13 @@ void ImDrawListSplitter::Merge(ImDrawList* draw_list) for (int i = 1; i < _Count; i++) { ImDrawChannel& ch = _Channels[i]; - - // Equivalent of PopUnusedDrawCmd() for this channel's cmdbuffer and except we don't need to test for UserCallback. - if (ch._CmdBuffer.Size > 0 && ch._CmdBuffer.back().ElemCount == 0) + if (ch._CmdBuffer.Size > 0 && ch._CmdBuffer.back().ElemCount == 0 && ch._CmdBuffer.back().UserCallback == NULL) // Equivalent of PopUnusedDrawCmd() ch._CmdBuffer.pop_back(); if (ch._CmdBuffer.Size > 0 && last_cmd != NULL) { + // Do not include ImDrawCmd_AreSequentialIdxOffset() in the compare as we rebuild IdxOffset values ourselves. + // Manipulating IdxOffset (e.g. by reordering draw commands like done by RenderDimmedBackgroundBehindWindow()) is not supported within a splitter. ImDrawCmd* next_cmd = &ch._CmdBuffer[0]; if (ImDrawCmd_HeaderCompare(last_cmd, next_cmd) == 0 && last_cmd->UserCallback == NULL && next_cmd->UserCallback == NULL) { @@ -1710,25 +1909,13 @@ void ImGui::ShadeVertsLinearUV(ImDrawList* draw_list, int vert_start_idx, int ve ImFontConfig::ImFontConfig() { - FontData = NULL; - FontDataSize = 0; + memset(this, 0, sizeof(*this)); FontDataOwnedByAtlas = true; - FontNo = 0; - SizePixels = 0.0f; OversampleH = 3; // FIXME: 2 may be a better default? OversampleV = 1; - PixelSnapH = false; - GlyphExtraSpacing = ImVec2(0.0f, 0.0f); - GlyphOffset = ImVec2(0.0f, 0.0f); - GlyphRanges = NULL; - GlyphMinAdvanceX = 0.0f; GlyphMaxAdvanceX = FLT_MAX; - MergeMode = false; - RasterizerFlags = 0x00; RasterizerMultiply = 1.0f; EllipsisChar = (ImWchar)-1; - memset(Name, 0, sizeof(Name)); - DstFont = NULL; } //----------------------------------------------------------------------------- @@ -1737,37 +1924,38 @@ ImFontConfig::ImFontConfig() // A work of art lies ahead! (. = white layer, X = black layer, others are blank) // The 2x2 white texels on the top left are the ones we'll use everywhere in Dear ImGui to render filled shapes. -const int FONT_ATLAS_DEFAULT_TEX_DATA_W = 108; // Actual texture will be 2 times that + 1 spacing. +// (This is used when io.MouseDrawCursor = true) +const int FONT_ATLAS_DEFAULT_TEX_DATA_W = 122; // Actual texture will be 2 times that + 1 spacing. const int FONT_ATLAS_DEFAULT_TEX_DATA_H = 27; static const char FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS[FONT_ATLAS_DEFAULT_TEX_DATA_W * FONT_ATLAS_DEFAULT_TEX_DATA_H + 1] = { - "..- -XXXXXXX- X - X -XXXXXXX - XXXXXXX- XX " - "..- -X.....X- X.X - X.X -X.....X - X.....X- X..X " - "--- -XXX.XXX- X...X - X...X -X....X - X....X- X..X " - "X - X.X - X.....X - X.....X -X...X - X...X- X..X " - "XX - X.X -X.......X- X.......X -X..X.X - X.X..X- X..X " - "X.X - X.X -XXXX.XXXX- XXXX.XXXX -X.X X.X - X.X X.X- X..XXX " - "X..X - X.X - X.X - X.X -XX X.X - X.X XX- X..X..XXX " - "X...X - X.X - X.X - XX X.X XX - X.X - X.X - X..X..X..XX " - "X....X - X.X - X.X - X.X X.X X.X - X.X - X.X - X..X..X..X.X " - "X.....X - X.X - X.X - X..X X.X X..X - X.X - X.X -XXX X..X..X..X..X" - "X......X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X XX-XX X.X -X..XX........X..X" - "X.......X - X.X - X.X -X.....................X- X.X X.X-X.X X.X -X...X...........X" - "X........X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X..X-X..X.X - X..............X" - "X.........X -XXX.XXX- X.X - X..X X.X X..X - X...X-X...X - X.............X" - "X..........X-X.....X- X.X - X.X X.X X.X - X....X-X....X - X.............X" - "X......XXXXX-XXXXXXX- X.X - XX X.X XX - X.....X-X.....X - X............X" - "X...X..X --------- X.X - X.X - XXXXXXX-XXXXXXX - X...........X " - "X..X X..X - -XXXX.XXXX- XXXX.XXXX ------------------------------------- X..........X " - "X.X X..X - -X.......X- X.......X - XX XX - - X..........X " - "XX X..X - - X.....X - X.....X - X.X X.X - - X........X " - " X..X - X...X - X...X - X..X X..X - - X........X " - " XX - X.X - X.X - X...XXXXXXXXXXXXX...X - - XXXXXXXXXX " - "------------ - X - X -X.....................X- ------------------" - " ----------------------------------- X...XXXXXXXXXXXXX...X - " - " - X..X X..X - " - " - X.X X.X - " - " - XX XX - " + "..- -XXXXXXX- X - X -XXXXXXX - XXXXXXX- XX - XX XX " + "..- -X.....X- X.X - X.X -X.....X - X.....X- X..X -X..X X..X" + "--- -XXX.XXX- X...X - X...X -X....X - X....X- X..X -X...X X...X" + "X - X.X - X.....X - X.....X -X...X - X...X- X..X - X...X X...X " + "XX - X.X -X.......X- X.......X -X..X.X - X.X..X- X..X - X...X...X " + "X.X - X.X -XXXX.XXXX- XXXX.XXXX -X.X X.X - X.X X.X- X..XXX - X.....X " + "X..X - X.X - X.X - X.X -XX X.X - X.X XX- X..X..XXX - X...X " + "X...X - X.X - X.X - XX X.X XX - X.X - X.X - X..X..X..XX - X.X " + "X....X - X.X - X.X - X.X X.X X.X - X.X - X.X - X..X..X..X.X - X...X " + "X.....X - X.X - X.X - X..X X.X X..X - X.X - X.X -XXX X..X..X..X..X- X.....X " + "X......X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X XX-XX X.X -X..XX........X..X- X...X...X " + "X.......X - X.X - X.X -X.....................X- X.X X.X-X.X X.X -X...X...........X- X...X X...X " + "X........X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X..X-X..X.X - X..............X-X...X X...X" + "X.........X -XXX.XXX- X.X - X..X X.X X..X - X...X-X...X - X.............X-X..X X..X" + "X..........X-X.....X- X.X - X.X X.X X.X - X....X-X....X - X.............X- XX XX " + "X......XXXXX-XXXXXXX- X.X - XX X.X XX - X.....X-X.....X - X............X--------------" + "X...X..X --------- X.X - X.X - XXXXXXX-XXXXXXX - X...........X - " + "X..X X..X - -XXXX.XXXX- XXXX.XXXX ------------------------------------- X..........X - " + "X.X X..X - -X.......X- X.......X - XX XX - - X..........X - " + "XX X..X - - X.....X - X.....X - X.X X.X - - X........X - " + " X..X - - X...X - X...X - X..X X..X - - X........X - " + " XX - - X.X - X.X - X...XXXXXXXXXXXXX...X - - XXXXXXXXXX - " + "------------- - X - X -X.....................X- ------------------- " + " ----------------------------------- X...XXXXXXXXXXXXX...X - " + " - X..X X..X - " + " - X.X X.X - " + " - XX XX - " }; static const ImVec2 FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[ImGuiMouseCursor_COUNT][3] = @@ -1781,21 +1969,13 @@ static const ImVec2 FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[ImGuiMouseCursor_COUNT][3 { ImVec2(73,0), ImVec2(17,17), ImVec2( 8, 8) }, // ImGuiMouseCursor_ResizeNESW { ImVec2(55,0), ImVec2(17,17), ImVec2( 8, 8) }, // ImGuiMouseCursor_ResizeNWSE { ImVec2(91,0), ImVec2(17,22), ImVec2( 5, 0) }, // ImGuiMouseCursor_Hand + { ImVec2(109,0),ImVec2(13,15), ImVec2( 6, 7) }, // ImGuiMouseCursor_NotAllowed }; ImFontAtlas::ImFontAtlas() { - Locked = false; - Flags = ImFontAtlasFlags_None; - TexID = (ImTextureID)NULL; - TexDesiredWidth = 0; + memset(this, 0, sizeof(*this)); TexGlyphPadding = 1; - - TexPixelsAlpha8 = NULL; - TexPixelsRGBA32 = NULL; - TexWidth = TexHeight = 0; - TexUvScale = ImVec2(0.0f, 0.0f); - TexUvWhitePixel = ImVec2(0.0f, 0.0f); PackIdMouseCursors = PackIdLines = -1; } @@ -1825,6 +2005,7 @@ void ImFontAtlas::ClearInputData() ConfigData.clear(); CustomRects.clear(); PackIdMouseCursors = PackIdLines = -1; + // Important: we leave TexReady untouched } void ImFontAtlas::ClearTexData() @@ -1836,14 +2017,15 @@ void ImFontAtlas::ClearTexData() IM_FREE(TexPixelsRGBA32); TexPixelsAlpha8 = NULL; TexPixelsRGBA32 = NULL; + TexPixelsUseColors = false; + // Important: we leave TexReady untouched } void ImFontAtlas::ClearFonts() { IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!"); - for (int i = 0; i < Fonts.Size; i++) - IM_DELETE(Fonts[i]); - Fonts.clear(); + Fonts.clear_delete(); + TexReady = false; } void ImFontAtlas::Clear() @@ -1857,11 +2039,7 @@ void ImFontAtlas::GetTexDataAsAlpha8(unsigned char** out_pixels, int* out_wid { // Build atlas on demand if (TexPixelsAlpha8 == NULL) - { - if (ConfigData.empty()) - AddFontDefault(); Build(); - } *out_pixels = TexPixelsAlpha8; if (out_width) *out_width = TexWidth; @@ -1920,6 +2098,7 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg) new_font_cfg.DstFont->EllipsisChar = font_cfg->EllipsisChar; // Invalidate texture + TexReady = false; ClearTexData(); return new_font_cfg.DstFont; } @@ -1991,7 +2170,7 @@ ImFont* ImFontAtlas::AddFontFromMemoryTTF(void* ttf_data, int ttf_size, float si IM_ASSERT(font_cfg.FontData == NULL); font_cfg.FontData = ttf_data; font_cfg.FontDataSize = ttf_size; - font_cfg.SizePixels = size_pixels; + font_cfg.SizePixels = size_pixels > 0.0f ? size_pixels : font_cfg.SizePixels; if (glyph_ranges) font_cfg.GlyphRanges = glyph_ranges; return AddFont(&font_cfg); @@ -2081,7 +2260,30 @@ bool ImFontAtlas::GetMouseCursorTexData(ImGuiMouseCursor cursor_type, ImVec2* ou bool ImFontAtlas::Build() { IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!"); - return ImFontAtlasBuildWithStbTruetype(this); + + // Default font is none are specified + if (ConfigData.Size == 0) + AddFontDefault(); + + // Select builder + // - Note that we do not reassign to atlas->FontBuilderIO, since it is likely to point to static data which + // may mess with some hot-reloading schemes. If you need to assign to this (for dynamic selection) AND are + // using a hot-reloading scheme that messes up static data, store your own instance of ImFontBuilderIO somewhere + // and point to it instead of pointing directly to return value of the GetBuilderXXX functions. + const ImFontBuilderIO* builder_io = FontBuilderIO; + if (builder_io == NULL) + { +#ifdef IMGUI_ENABLE_FREETYPE + builder_io = ImGuiFreeType::GetBuilderForFreeType(); +#elif defined(IMGUI_ENABLE_STB_TRUETYPE) + builder_io = ImFontAtlasGetBuilderForStbTruetype(); +#else + IM_ASSERT(0); // Invalid Build function +#endif + } + + // Build + return builder_io->FontBuilder_Build(this); } void ImFontAtlasBuildMultiplyCalcLookupTable(unsigned char out_table[256], float in_brighten_factor) @@ -2101,6 +2303,7 @@ void ImFontAtlasBuildMultiplyRectAlpha8(const unsigned char table[256], unsig data[i] = table[data[i]]; } +#ifdef IMGUI_ENABLE_STB_TRUETYPE // Temporary data for one source font (multiple source fonts can be merged into one destination ImFont) // (C++03 doesn't allow instancing ImVector<> with function-local types so we declare the type here.) struct ImFontBuildSrcData @@ -2138,7 +2341,7 @@ static void UnpackBitVectorToFlatIndexList(const ImBitVector* in, ImVector* out->push_back((int)(((it - it_begin) << 5) + bit_n)); } -bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas) +static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas) { IM_ASSERT(atlas->ConfigData.Size > 0); @@ -2383,14 +2586,22 @@ bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas) } } - // Cleanup temporary (ImVector doesn't honor destructor) - for (int src_i = 0; src_i < src_tmp_array.Size; src_i++) - src_tmp_array[src_i].~ImFontBuildSrcData(); + // Cleanup + src_tmp_array.clear_destruct(); ImFontAtlasBuildFinish(atlas); return true; } +const ImFontBuilderIO* ImFontAtlasGetBuilderForStbTruetype() +{ + static ImFontBuilderIO io; + io.FontBuilder_Build = ImFontAtlasBuildWithStbTruetype; + return &io; +} + +#endif // IMGUI_ENABLE_STB_TRUETYPE + void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* font_config, float ascent, float descent) { if (!font_config->MergeMode) @@ -2433,7 +2644,7 @@ void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* stbrp_context_opa } } -void ImFontAtlasBuildRender1bppRectFromString(ImFontAtlas* atlas, int x, int y, int w, int h, const char* in_str, char in_marker_char, unsigned char in_marker_pixel_value) +void ImFontAtlasBuildRender8bppRectFromString(ImFontAtlas* atlas, int x, int y, int w, int h, const char* in_str, char in_marker_char, unsigned char in_marker_pixel_value) { IM_ASSERT(x >= 0 && x + w <= atlas->TexWidth); IM_ASSERT(y >= 0 && y + h <= atlas->TexHeight); @@ -2443,6 +2654,16 @@ void ImFontAtlasBuildRender1bppRectFromString(ImFontAtlas* atlas, int x, int y, out_pixel[off_x] = (in_str[off_x] == in_marker_char) ? in_marker_pixel_value : 0x00; } +void ImFontAtlasBuildRender32bppRectFromString(ImFontAtlas* atlas, int x, int y, int w, int h, const char* in_str, char in_marker_char, unsigned int in_marker_pixel_value) +{ + IM_ASSERT(x >= 0 && x + w <= atlas->TexWidth); + IM_ASSERT(y >= 0 && y + h <= atlas->TexHeight); + unsigned int* out_pixel = atlas->TexPixelsRGBA32 + x + (y * atlas->TexWidth); + for (int off_y = 0; off_y < h; off_y++, out_pixel += atlas->TexWidth, in_str += w) + for (int off_x = 0; off_x < w; off_x++) + out_pixel[off_x] = (in_str[off_x] == in_marker_char) ? in_marker_pixel_value : IM_COL32_BLACK_TRANS; +} + static void ImFontAtlasBuildRenderDefaultTexData(ImFontAtlas* atlas) { ImFontAtlasCustomRect* r = atlas->GetCustomRectByIndex(atlas->PackIdMouseCursors); @@ -2455,15 +2676,30 @@ static void ImFontAtlasBuildRenderDefaultTexData(ImFontAtlas* atlas) IM_ASSERT(r->Width == FONT_ATLAS_DEFAULT_TEX_DATA_W * 2 + 1 && r->Height == FONT_ATLAS_DEFAULT_TEX_DATA_H); const int x_for_white = r->X; const int x_for_black = r->X + FONT_ATLAS_DEFAULT_TEX_DATA_W + 1; - ImFontAtlasBuildRender1bppRectFromString(atlas, x_for_white, r->Y, FONT_ATLAS_DEFAULT_TEX_DATA_W, FONT_ATLAS_DEFAULT_TEX_DATA_H, FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS, '.', 0xFF); - ImFontAtlasBuildRender1bppRectFromString(atlas, x_for_black, r->Y, FONT_ATLAS_DEFAULT_TEX_DATA_W, FONT_ATLAS_DEFAULT_TEX_DATA_H, FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS, 'X', 0xFF); + if (atlas->TexPixelsAlpha8 != NULL) + { + ImFontAtlasBuildRender8bppRectFromString(atlas, x_for_white, r->Y, FONT_ATLAS_DEFAULT_TEX_DATA_W, FONT_ATLAS_DEFAULT_TEX_DATA_H, FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS, '.', 0xFF); + ImFontAtlasBuildRender8bppRectFromString(atlas, x_for_black, r->Y, FONT_ATLAS_DEFAULT_TEX_DATA_W, FONT_ATLAS_DEFAULT_TEX_DATA_H, FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS, 'X', 0xFF); + } + else + { + ImFontAtlasBuildRender32bppRectFromString(atlas, x_for_white, r->Y, FONT_ATLAS_DEFAULT_TEX_DATA_W, FONT_ATLAS_DEFAULT_TEX_DATA_H, FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS, '.', IM_COL32_WHITE); + ImFontAtlasBuildRender32bppRectFromString(atlas, x_for_black, r->Y, FONT_ATLAS_DEFAULT_TEX_DATA_W, FONT_ATLAS_DEFAULT_TEX_DATA_H, FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS, 'X', IM_COL32_WHITE); + } } else { // Render 4 white pixels IM_ASSERT(r->Width == 2 && r->Height == 2); const int offset = (int)r->X + (int)r->Y * w; - atlas->TexPixelsAlpha8[offset] = atlas->TexPixelsAlpha8[offset + 1] = atlas->TexPixelsAlpha8[offset + w] = atlas->TexPixelsAlpha8[offset + w + 1] = 0xFF; + if (atlas->TexPixelsAlpha8 != NULL) + { + atlas->TexPixelsAlpha8[offset] = atlas->TexPixelsAlpha8[offset + 1] = atlas->TexPixelsAlpha8[offset + w] = atlas->TexPixelsAlpha8[offset + w + 1] = 0xFF; + } + else + { + atlas->TexPixelsRGBA32[offset] = atlas->TexPixelsRGBA32[offset + 1] = atlas->TexPixelsRGBA32[offset + w] = atlas->TexPixelsRGBA32[offset + w + 1] = IM_COL32_WHITE; + } } atlas->TexUvWhitePixel = ImVec2((r->X + 0.5f) * atlas->TexUvScale.x, (r->Y + 0.5f) * atlas->TexUvScale.y); } @@ -2486,10 +2722,30 @@ static void ImFontAtlasBuildRenderLinesTexData(ImFontAtlas* atlas) // Write each slice IM_ASSERT(pad_left + line_width + pad_right == r->Width && y < r->Height); // Make sure we're inside the texture bounds before we start writing pixels - unsigned char* write_ptr = &atlas->TexPixelsAlpha8[r->X + ((r->Y + y) * atlas->TexWidth)]; - memset(write_ptr, 0x00, pad_left); - memset(write_ptr + pad_left, 0xFF, line_width); - memset(write_ptr + pad_left + line_width, 0x00, pad_right); + if (atlas->TexPixelsAlpha8 != NULL) + { + unsigned char* write_ptr = &atlas->TexPixelsAlpha8[r->X + ((r->Y + y) * atlas->TexWidth)]; + for (unsigned int i = 0; i < pad_left; i++) + *(write_ptr + i) = 0x00; + + for (unsigned int i = 0; i < line_width; i++) + *(write_ptr + pad_left + i) = 0xFF; + + for (unsigned int i = 0; i < pad_right; i++) + *(write_ptr + pad_left + line_width + i) = 0x00; + } + else + { + unsigned int* write_ptr = &atlas->TexPixelsRGBA32[r->X + ((r->Y + y) * atlas->TexWidth)]; + for (unsigned int i = 0; i < pad_left; i++) + *(write_ptr + i) = IM_COL32_BLACK_TRANS; + + for (unsigned int i = 0; i < line_width; i++) + *(write_ptr + pad_left + i) = IM_COL32_WHITE; + + for (unsigned int i = 0; i < pad_right; i++) + *(write_ptr + pad_left + line_width + i) = IM_COL32_BLACK_TRANS; + } // Calculate UVs for this line ImVec2 uv0 = ImVec2((float)(r->X + pad_left - 1), (float)(r->Y + y)) * atlas->TexUvScale; @@ -2524,7 +2780,7 @@ void ImFontAtlasBuildInit(ImFontAtlas* atlas) void ImFontAtlasBuildFinish(ImFontAtlas* atlas) { // Render into our custom data blocks - IM_ASSERT(atlas->TexPixelsAlpha8 != NULL); + IM_ASSERT(atlas->TexPixelsAlpha8 != NULL || atlas->TexPixelsRGBA32 != NULL); ImFontAtlasBuildRenderDefaultTexData(atlas); ImFontAtlasBuildRenderLinesTexData(atlas); @@ -2547,22 +2803,7 @@ void ImFontAtlasBuildFinish(ImFontAtlas* atlas) if (atlas->Fonts[i]->DirtyLookupTables) atlas->Fonts[i]->BuildLookupTable(); - // Ellipsis character is required for rendering elided text. We prefer using U+2026 (horizontal ellipsis). - // However some old fonts may contain ellipsis at U+0085. Here we auto-detect most suitable ellipsis character. - // FIXME: Also note that 0x2026 is currently seldom included in our font ranges. Because of this we are more likely to use three individual dots. - for (int i = 0; i < atlas->Fonts.size(); i++) - { - ImFont* font = atlas->Fonts[i]; - if (font->EllipsisChar != (ImWchar)-1) - continue; - const ImWchar ellipsis_variants[] = { (ImWchar)0x2026, (ImWchar)0x0085 }; - for (int j = 0; j < IM_ARRAYSIZE(ellipsis_variants); j++) - if (font->FindGlyphNoFallback(ellipsis_variants[j]) != NULL) // Verify glyph exists - { - font->EllipsisChar = ellipsis_variants[j]; - break; - } - } + atlas->TexReady = true; } // Retrieve list of range (2 int per range, values are inclusive) @@ -2583,6 +2824,7 @@ const ImWchar* ImFontAtlas::GetGlyphRangesKorean() 0x0020, 0x00FF, // Basic Latin + Latin Supplement 0x3131, 0x3163, // Korean alphabets 0xAC00, 0xD7A3, // Korean characters + 0xFFFD, 0xFFFD, // Invalid 0, }; return &ranges[0]; @@ -2597,6 +2839,7 @@ const ImWchar* ImFontAtlas::GetGlyphRangesChineseFull() 0x3000, 0x30FF, // CJK Symbols and Punctuations, Hiragana, Katakana 0x31F0, 0x31FF, // Katakana Phonetic Extensions 0xFF00, 0xFFEF, // Half-width characters + 0xFFFD, 0xFFFD, // Invalid 0x4e00, 0x9FAF, // CJK Ideograms 0, }; @@ -2673,7 +2916,8 @@ const ImWchar* ImFontAtlas::GetGlyphRangesChineseSimplifiedCommon() 0x2000, 0x206F, // General Punctuation 0x3000, 0x30FF, // CJK Symbols and Punctuations, Hiragana, Katakana 0x31F0, 0x31FF, // Katakana Phonetic Extensions - 0xFF00, 0xFFEF // Half-width characters + 0xFF00, 0xFFEF, // Half-width characters + 0xFFFD, 0xFFFD // Invalid }; static ImWchar full_ranges[IM_ARRAYSIZE(base_ranges) + IM_ARRAYSIZE(accumulative_offsets_from_0x4E00) * 2 + 1] = { 0 }; if (!full_ranges[0]) @@ -2762,7 +3006,8 @@ const ImWchar* ImFontAtlas::GetGlyphRangesJapanese() 0x0020, 0x00FF, // Basic Latin + Latin Supplement 0x3000, 0x30FF, // CJK Symbols and Punctuations, Hiragana, Katakana 0x31F0, 0x31FF, // Katakana Phonetic Extensions - 0xFF00, 0xFFEF // Half-width characters + 0xFF00, 0xFFEF, // Half-width characters + 0xFFFD, 0xFFFD // Invalid }; static ImWchar full_ranges[IM_ARRAYSIZE(base_ranges) + IM_ARRAYSIZE(accumulative_offsets_from_0x4E00)*2 + 1] = { 0 }; if (!full_ranges[0]) @@ -2835,8 +3080,8 @@ void ImFontGlyphRangesBuilder::AddText(const char* text, const char* text_end) void ImFontGlyphRangesBuilder::AddRanges(const ImWchar* ranges) { for (; ranges[0]; ranges += 2) - for (ImWchar c = ranges[0]; c <= ranges[1]; c++) - AddChar(c); + for (unsigned int c = ranges[0]; c <= ranges[1] && c <= IM_UNICODE_CODEPOINT_MAX; c++) //-V560 + AddChar((ImWchar)c); } void ImFontGlyphRangesBuilder::BuildRanges(ImVector* out_ranges) @@ -2861,8 +3106,9 @@ ImFont::ImFont() { FontSize = 0.0f; FallbackAdvanceX = 0.0f; - FallbackChar = (ImWchar)'?'; + FallbackChar = (ImWchar)-1; EllipsisChar = (ImWchar)-1; + DotChar = (ImWchar)-1; FallbackGlyph = NULL; ContainerAtlas = NULL; ConfigData = NULL; @@ -2893,6 +3139,14 @@ void ImFont::ClearOutputData() MetricsTotalSurface = 0; } +static ImWchar FindFirstExistingGlyph(ImFont* font, const ImWchar* candidate_chars, int candidate_chars_count) +{ + for (int n = 0; n < candidate_chars_count; n++) + if (font->FindGlyphNoFallback(candidate_chars[n]) != NULL) + return candidate_chars[n]; + return (ImWchar)-1; +} + void ImFont::BuildLookupTable() { int max_codepoint = 0; @@ -2935,9 +3189,31 @@ void ImFont::BuildLookupTable() SetGlyphVisible((ImWchar)' ', false); SetGlyphVisible((ImWchar)'\t', false); - // Setup fall-backs + // Ellipsis character is required for rendering elided text. We prefer using U+2026 (horizontal ellipsis). + // However some old fonts may contain ellipsis at U+0085. Here we auto-detect most suitable ellipsis character. + // FIXME: Note that 0x2026 is rarely included in our font ranges. Because of this we are more likely to use three individual dots. + const ImWchar ellipsis_chars[] = { (ImWchar)0x2026, (ImWchar)0x0085 }; + const ImWchar dots_chars[] = { (ImWchar)'.', (ImWchar)0xFF0E }; + if (EllipsisChar == (ImWchar)-1) + EllipsisChar = FindFirstExistingGlyph(this, ellipsis_chars, IM_ARRAYSIZE(ellipsis_chars)); + if (DotChar == (ImWchar)-1) + DotChar = FindFirstExistingGlyph(this, dots_chars, IM_ARRAYSIZE(dots_chars)); + + // Setup fallback character + const ImWchar fallback_chars[] = { (ImWchar)IM_UNICODE_CODEPOINT_INVALID, (ImWchar)'?', (ImWchar)' ' }; FallbackGlyph = FindGlyphNoFallback(FallbackChar); - FallbackAdvanceX = FallbackGlyph ? FallbackGlyph->AdvanceX : 0.0f; + if (FallbackGlyph == NULL) + { + FallbackChar = FindFirstExistingGlyph(this, fallback_chars, IM_ARRAYSIZE(fallback_chars)); + FallbackGlyph = FindGlyphNoFallback(FallbackChar); + if (FallbackGlyph == NULL) + { + FallbackGlyph = &Glyphs.back(); + FallbackChar = (ImWchar)FallbackGlyph->Codepoint; + } + } + + FallbackAdvanceX = FallbackGlyph->AdvanceX; for (int i = 0; i < max_codepoint + 1; i++) if (IndexAdvanceX[i] < 0.0f) IndexAdvanceX[i] = FallbackAdvanceX; @@ -2962,12 +3238,6 @@ void ImFont::SetGlyphVisible(ImWchar c, bool visible) glyph->Visible = visible ? 1 : 0; } -void ImFont::SetFallbackChar(ImWchar c) -{ - FallbackChar = c; - BuildLookupTable(); -} - void ImFont::GrowIndex(int new_size) { IM_ASSERT(IndexAdvanceX.Size == IndexLookup.Size); @@ -3006,6 +3276,7 @@ void ImFont::AddGlyph(const ImFontConfig* cfg, ImWchar codepoint, float x0, floa ImFontGlyph& glyph = Glyphs.back(); glyph.Codepoint = (unsigned int)codepoint; glyph.Visible = (x0 != x1) && (y0 != y1); + glyph.Colored = false; glyph.X0 = x0; glyph.Y0 = y0; glyph.X1 = x1; @@ -3251,11 +3522,14 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons return text_size; } +// Note: as with every ImDrawList drawing function, this expects that the font atlas texture is bound. void ImFont::RenderChar(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, ImWchar c) const { const ImFontGlyph* glyph = FindGlyph(c); if (!glyph || !glyph->Visible) return; + if (glyph->Colored) + col |= ~IM_COL32_A_MASK; float scale = (size >= 0.0f) ? (size / FontSize) : 1.0f; pos.x = IM_FLOOR(pos.x); pos.y = IM_FLOOR(pos.y); @@ -3263,6 +3537,7 @@ void ImFont::RenderChar(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col draw_list->PrimRectUV(ImVec2(pos.x + glyph->X0 * scale, pos.y + glyph->Y0 * scale), ImVec2(pos.x + glyph->X1 * scale, pos.y + glyph->Y1 * scale), ImVec2(glyph->U0, glyph->V0), ImVec2(glyph->U1, glyph->V1), col); } +// Note: as with every ImDrawList drawing function, this expects that the font atlas texture is bound. void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width, bool cpu_fine_clip) const { if (!text_end) @@ -3318,6 +3593,8 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col ImDrawIdx* idx_write = draw_list->_IdxWritePtr; unsigned int vtx_current_idx = draw_list->_VtxCurrentIdx; + const ImU32 col_untinted = col | ~IM_COL32_A_MASK; + while (s < text_end) { if (word_wrap_enabled) @@ -3423,14 +3700,17 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col } } + // Support for untinted glyphs + ImU32 glyph_col = glyph->Colored ? col_untinted : col; + // We are NOT calling PrimRectUV() here because non-inlined causes too much overhead in a debug builds. Inlined here: { idx_write[0] = (ImDrawIdx)(vtx_current_idx); idx_write[1] = (ImDrawIdx)(vtx_current_idx+1); idx_write[2] = (ImDrawIdx)(vtx_current_idx+2); idx_write[3] = (ImDrawIdx)(vtx_current_idx); idx_write[4] = (ImDrawIdx)(vtx_current_idx+2); idx_write[5] = (ImDrawIdx)(vtx_current_idx+3); - vtx_write[0].pos.x = x1; vtx_write[0].pos.y = y1; vtx_write[0].col = col; vtx_write[0].uv.x = u1; vtx_write[0].uv.y = v1; - vtx_write[1].pos.x = x2; vtx_write[1].pos.y = y1; vtx_write[1].col = col; vtx_write[1].uv.x = u2; vtx_write[1].uv.y = v1; - vtx_write[2].pos.x = x2; vtx_write[2].pos.y = y2; vtx_write[2].col = col; vtx_write[2].uv.x = u2; vtx_write[2].uv.y = v2; - vtx_write[3].pos.x = x1; vtx_write[3].pos.y = y2; vtx_write[3].col = col; vtx_write[3].uv.x = u1; vtx_write[3].uv.y = v2; + vtx_write[0].pos.x = x1; vtx_write[0].pos.y = y1; vtx_write[0].col = glyph_col; vtx_write[0].uv.x = u1; vtx_write[0].uv.y = v1; + vtx_write[1].pos.x = x2; vtx_write[1].pos.y = y1; vtx_write[1].col = glyph_col; vtx_write[1].uv.x = u2; vtx_write[1].uv.y = v1; + vtx_write[2].pos.x = x2; vtx_write[2].pos.y = y2; vtx_write[2].col = glyph_col; vtx_write[2].uv.x = u2; vtx_write[2].uv.y = v2; + vtx_write[3].pos.x = x1; vtx_write[3].pos.y = y2; vtx_write[3].col = glyph_col; vtx_write[3].uv.x = u1; vtx_write[3].uv.y = v2; vtx_write += 4; vtx_current_idx += 4; idx_write += 6; @@ -3459,6 +3739,7 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col // - RenderMouseCursor() // - RenderArrowPointingAt() // - RenderRectFilledRangeH() +// - RenderRectFilledWithHole() //----------------------------------------------------------------------------- // Function in need of a redesign (legacy mess) // - RenderColorRectWithAlphaCheckerboard() @@ -3513,7 +3794,7 @@ void ImGui::RenderCheckMark(ImDrawList* draw_list, ImVec2 pos, ImU32 col, float draw_list->PathLineTo(ImVec2(bx - third, by - third)); draw_list->PathLineTo(ImVec2(bx, by)); draw_list->PathLineTo(ImVec2(bx + third * 2.0f, by - third * 2.0f)); - draw_list->PathStroke(col, false, thickness); + draw_list->PathStroke(col, 0, thickness); } void ImGui::RenderMouseCursor(ImDrawList* draw_list, ImVec2 pos, float scale, ImGuiMouseCursor mouse_cursor, ImU32 col_fill, ImU32 col_border, ImU32 col_shadow) @@ -3527,7 +3808,7 @@ void ImGui::RenderMouseCursor(ImDrawList* draw_list, ImVec2 pos, float scale, Im if (font_atlas->GetMouseCursorTexData(mouse_cursor, &offset, &size, &uv[0], &uv[2])) { pos -= offset; - const ImTextureID tex_id = font_atlas->TexID; + ImTextureID tex_id = font_atlas->TexID; draw_list->PushTextureID(tex_id); draw_list->AddImage(tex_id, pos + ImVec2(1, 0) * scale, pos + (ImVec2(1, 0) + size) * scale, uv[2], uv[3], col_shadow); draw_list->AddImage(tex_id, pos + ImVec2(2, 0) * scale, pos + (ImVec2(2, 0) + size) * scale, uv[2], uv[3], col_shadow); @@ -3625,27 +3906,29 @@ void ImGui::RenderRectFilledWithHole(ImDrawList* draw_list, ImRect outer, ImRect const bool fill_R = (inner.Max.x < outer.Max.x); const bool fill_U = (inner.Min.y > outer.Min.y); const bool fill_D = (inner.Max.y < outer.Max.y); - if (fill_L) draw_list->AddRectFilled(ImVec2(outer.Min.x, inner.Min.y), ImVec2(inner.Min.x, inner.Max.y), col, rounding, (fill_U ? 0 : ImDrawCornerFlags_TopLeft) | (fill_D ? 0 : ImDrawCornerFlags_BotLeft)); - if (fill_R) draw_list->AddRectFilled(ImVec2(inner.Max.x, inner.Min.y), ImVec2(outer.Max.x, inner.Max.y), col, rounding, (fill_U ? 0 : ImDrawCornerFlags_TopRight) | (fill_D ? 0 : ImDrawCornerFlags_BotRight)); - if (fill_U) draw_list->AddRectFilled(ImVec2(inner.Min.x, outer.Min.y), ImVec2(inner.Max.x, inner.Min.y), col, rounding, (fill_L ? 0 : ImDrawCornerFlags_TopLeft) | (fill_R ? 0 : ImDrawCornerFlags_TopRight)); - if (fill_D) draw_list->AddRectFilled(ImVec2(inner.Min.x, inner.Max.y), ImVec2(inner.Max.x, outer.Max.y), col, rounding, (fill_L ? 0 : ImDrawCornerFlags_BotLeft) | (fill_R ? 0 : ImDrawCornerFlags_BotRight)); - if (fill_L && fill_U) draw_list->AddRectFilled(ImVec2(outer.Min.x, outer.Min.y), ImVec2(inner.Min.x, inner.Min.y), col, rounding, ImDrawCornerFlags_TopLeft); - if (fill_R && fill_U) draw_list->AddRectFilled(ImVec2(inner.Max.x, outer.Min.y), ImVec2(outer.Max.x, inner.Min.y), col, rounding, ImDrawCornerFlags_TopRight); - if (fill_L && fill_D) draw_list->AddRectFilled(ImVec2(outer.Min.x, inner.Max.y), ImVec2(inner.Min.x, outer.Max.y), col, rounding, ImDrawCornerFlags_BotLeft); - if (fill_R && fill_D) draw_list->AddRectFilled(ImVec2(inner.Max.x, inner.Max.y), ImVec2(outer.Max.x, outer.Max.y), col, rounding, ImDrawCornerFlags_BotRight); + if (fill_L) draw_list->AddRectFilled(ImVec2(outer.Min.x, inner.Min.y), ImVec2(inner.Min.x, inner.Max.y), col, rounding, ImDrawFlags_RoundCornersNone | (fill_U ? 0 : ImDrawFlags_RoundCornersTopLeft) | (fill_D ? 0 : ImDrawFlags_RoundCornersBottomLeft)); + if (fill_R) draw_list->AddRectFilled(ImVec2(inner.Max.x, inner.Min.y), ImVec2(outer.Max.x, inner.Max.y), col, rounding, ImDrawFlags_RoundCornersNone | (fill_U ? 0 : ImDrawFlags_RoundCornersTopRight) | (fill_D ? 0 : ImDrawFlags_RoundCornersBottomRight)); + if (fill_U) draw_list->AddRectFilled(ImVec2(inner.Min.x, outer.Min.y), ImVec2(inner.Max.x, inner.Min.y), col, rounding, ImDrawFlags_RoundCornersNone | (fill_L ? 0 : ImDrawFlags_RoundCornersTopLeft) | (fill_R ? 0 : ImDrawFlags_RoundCornersTopRight)); + if (fill_D) draw_list->AddRectFilled(ImVec2(inner.Min.x, inner.Max.y), ImVec2(inner.Max.x, outer.Max.y), col, rounding, ImDrawFlags_RoundCornersNone | (fill_L ? 0 : ImDrawFlags_RoundCornersBottomLeft) | (fill_R ? 0 : ImDrawFlags_RoundCornersBottomRight)); + if (fill_L && fill_U) draw_list->AddRectFilled(ImVec2(outer.Min.x, outer.Min.y), ImVec2(inner.Min.x, inner.Min.y), col, rounding, ImDrawFlags_RoundCornersTopLeft); + if (fill_R && fill_U) draw_list->AddRectFilled(ImVec2(inner.Max.x, outer.Min.y), ImVec2(outer.Max.x, inner.Min.y), col, rounding, ImDrawFlags_RoundCornersTopRight); + if (fill_L && fill_D) draw_list->AddRectFilled(ImVec2(outer.Min.x, inner.Max.y), ImVec2(inner.Min.x, outer.Max.y), col, rounding, ImDrawFlags_RoundCornersBottomLeft); + if (fill_R && fill_D) draw_list->AddRectFilled(ImVec2(inner.Max.x, inner.Max.y), ImVec2(outer.Max.x, outer.Max.y), col, rounding, ImDrawFlags_RoundCornersBottomRight); } // Helper for ColorPicker4() // NB: This is rather brittle and will show artifact when rounding this enabled if rounded corners overlap multiple cells. Caller currently responsible for avoiding that. // Spent a non reasonable amount of time trying to getting this right for ColorButton with rounding+anti-aliasing+ImGuiColorEditFlags_HalfAlphaPreview flag + various grid sizes and offsets, and eventually gave up... probably more reasonable to disable rounding altogether. // FIXME: uses ImGui::GetColorU32 -void ImGui::RenderColorRectWithAlphaCheckerboard(ImDrawList* draw_list, ImVec2 p_min, ImVec2 p_max, ImU32 col, float grid_step, ImVec2 grid_off, float rounding, int rounding_corners_flags) +void ImGui::RenderColorRectWithAlphaCheckerboard(ImDrawList* draw_list, ImVec2 p_min, ImVec2 p_max, ImU32 col, float grid_step, ImVec2 grid_off, float rounding, ImDrawFlags flags) { + if ((flags & ImDrawFlags_RoundCornersMask_) == 0) + flags = ImDrawFlags_RoundCornersDefault_; if (((col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT) < 0xFF) { - ImU32 col_bg1 = ImGui::GetColorU32(ImAlphaBlendColors(IM_COL32(204, 204, 204, 255), col)); - ImU32 col_bg2 = ImGui::GetColorU32(ImAlphaBlendColors(IM_COL32(128, 128, 128, 255), col)); - draw_list->AddRectFilled(p_min, p_max, col_bg1, rounding, rounding_corners_flags); + ImU32 col_bg1 = GetColorU32(ImAlphaBlendColors(IM_COL32(204, 204, 204, 255), col)); + ImU32 col_bg2 = GetColorU32(ImAlphaBlendColors(IM_COL32(128, 128, 128, 255), col)); + draw_list->AddRectFilled(p_min, p_max, col_bg1, rounding, flags); int yi = 0; for (float y = p_min.y + grid_off.y; y < p_max.y; y += grid_step, yi++) @@ -3658,17 +3941,19 @@ void ImGui::RenderColorRectWithAlphaCheckerboard(ImDrawList* draw_list, ImVec2 p float x1 = ImClamp(x, p_min.x, p_max.x), x2 = ImMin(x + grid_step, p_max.x); if (x2 <= x1) continue; - int rounding_corners_flags_cell = 0; - if (y1 <= p_min.y) { if (x1 <= p_min.x) rounding_corners_flags_cell |= ImDrawCornerFlags_TopLeft; if (x2 >= p_max.x) rounding_corners_flags_cell |= ImDrawCornerFlags_TopRight; } - if (y2 >= p_max.y) { if (x1 <= p_min.x) rounding_corners_flags_cell |= ImDrawCornerFlags_BotLeft; if (x2 >= p_max.x) rounding_corners_flags_cell |= ImDrawCornerFlags_BotRight; } - rounding_corners_flags_cell &= rounding_corners_flags; - draw_list->AddRectFilled(ImVec2(x1, y1), ImVec2(x2, y2), col_bg2, rounding_corners_flags_cell ? rounding : 0.0f, rounding_corners_flags_cell); + ImDrawFlags cell_flags = ImDrawFlags_RoundCornersNone; + if (y1 <= p_min.y) { if (x1 <= p_min.x) cell_flags |= ImDrawFlags_RoundCornersTopLeft; if (x2 >= p_max.x) cell_flags |= ImDrawFlags_RoundCornersTopRight; } + if (y2 >= p_max.y) { if (x1 <= p_min.x) cell_flags |= ImDrawFlags_RoundCornersBottomLeft; if (x2 >= p_max.x) cell_flags |= ImDrawFlags_RoundCornersBottomRight; } + + // Combine flags + cell_flags = (flags == ImDrawFlags_RoundCornersNone || cell_flags == ImDrawFlags_RoundCornersNone) ? ImDrawFlags_RoundCornersNone : (cell_flags & flags); + draw_list->AddRectFilled(ImVec2(x1, y1), ImVec2(x2, y2), col_bg2, rounding, cell_flags); } } } else { - draw_list->AddRectFilled(p_min, p_max, col, rounding, rounding_corners_flags); + draw_list->AddRectFilled(p_min, p_max, col, rounding, flags); } } diff --git a/apps/common/external/imgui/imgui_internal.h b/apps/common/external/imgui/imgui_internal.h index dff845a635..baa4a4de95 100644 --- a/apps/common/external/imgui/imgui_internal.h +++ b/apps/common/external/imgui/imgui_internal.h @@ -1,4 +1,4 @@ -// dear imgui, v1.80 +// dear imgui, v1.87 // (internal structures/api) // You may use this file to debug, understand or extend ImGui features but we don't provide any guarantee of forward compatibility! @@ -18,18 +18,22 @@ Index of this file: // [SECTION] Generic helpers // [SECTION] ImDrawList support // [SECTION] Widgets support: flags, enums, data structures +// [SECTION] Inputs support +// [SECTION] Clipper support +// [SECTION] Navigation support // [SECTION] Columns support // [SECTION] Multi-select support // [SECTION] Docking support // [SECTION] Viewport support // [SECTION] Settings support -// [SECTION] Metrics, Debug +// [SECTION] Metrics, Debug tools // [SECTION] Generic context hooks // [SECTION] ImGuiContext (main imgui context) // [SECTION] ImGuiWindowTempData, ImGuiWindow // [SECTION] Tab bar, Tab item support // [SECTION] Table support -// [SECTION] Internal API +// [SECTION] ImGui internal API +// [SECTION] ImFontAtlas internal API // [SECTION] Test Engine specific hooks (imgui_test_engine) */ @@ -42,7 +46,7 @@ Index of this file: //----------------------------------------------------------------------------- #ifndef IMGUI_VERSION -#error Must include imgui.h before imgui_internal.h +#include "imgui.h" #endif #include // FILE*, sscanf @@ -50,10 +54,21 @@ Index of this file: #include // sqrtf, fabsf, fmodf, powf, floorf, ceilf, cosf, sinf #include // INT_MIN, INT_MAX +// Enable SSE intrinsics if available +#if (defined __SSE__ || defined __x86_64__ || defined _M_X64) && !defined(IMGUI_DISABLE_SSE) +#define IMGUI_ENABLE_SSE +#include +#endif + // Visual Studio warnings #ifdef _MSC_VER #pragma warning (push) -#pragma warning (disable: 4251) // class 'xxx' needs to have dll-interface to be used by clients of struct 'xxx' // when IMGUI_API is set to__declspec(dllexport) +#pragma warning (disable: 4251) // class 'xxx' needs to have dll-interface to be used by clients of struct 'xxx' // when IMGUI_API is set to__declspec(dllexport) +#pragma warning (disable: 26812) // The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). [MSVC Static Analyzer) +#pragma warning (disable: 26495) // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6). +#if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later +#pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types +#endif #endif // Clang/GCC warnings with -Weverything @@ -63,12 +78,14 @@ Index of this file: #pragma clang diagnostic ignored "-Wunknown-warning-option" // warning: unknown warning group 'xxx' #endif #pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx' +#pragma clang diagnostic ignored "-Wfloat-equal" // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants ok, for ImFloorSigned() #pragma clang diagnostic ignored "-Wunused-function" // for stb_textedit.h #pragma clang diagnostic ignored "-Wmissing-prototypes" // for stb_textedit.h #pragma clang diagnostic ignored "-Wold-style-cast" #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" #pragma clang diagnostic ignored "-Wdouble-promotion" #pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision +#pragma clang diagnostic ignored "-Wmissing-noreturn" // warning: function 'xxx' could be declared with attribute 'noreturn' #elif defined(__GNUC__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind @@ -83,6 +100,12 @@ Index of this file: #error Use IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS #endif +// Enable stb_truetype by default unless FreeType is enabled. +// You can compile with both by defining both IMGUI_ENABLE_FREETYPE and IMGUI_ENABLE_STB_TRUETYPE together. +#ifndef IMGUI_ENABLE_FREETYPE +#define IMGUI_ENABLE_STB_TRUETYPE +#endif + //----------------------------------------------------------------------------- // [SECTION] Forward declarations //----------------------------------------------------------------------------- @@ -97,9 +120,9 @@ struct ImGuiContextHook; // Hook for extensions like ImGuiTestEngine struct ImGuiDataTypeInfo; // Type information associated to a ImGuiDataType enum struct ImGuiGroupData; // Stacked storage data for BeginGroup()/EndGroup() struct ImGuiInputTextState; // Internal state of the currently focused/edited text input box -struct ImGuiLastItemDataBackup; // Backup and restore IsItemHovered() internal data +struct ImGuiLastItemData; // Status storage for last submitted items struct ImGuiMenuColumns; // Simple column measurement, currently used for MenuItem() only -struct ImGuiNavMoveResult; // Result of a gamepad/keyboard directional navigation move query result +struct ImGuiNavItemData; // Result of a gamepad/keyboard directional navigation move query result struct ImGuiMetricsConfig; // Storage for ShowMetricsWindow() and DebugNodeXXX() functions struct ImGuiNextWindowData; // Storage for SetNextWindow** functions struct ImGuiNextItemData; // Storage for SetNextItem** functions @@ -113,14 +136,16 @@ struct ImGuiTabBar; // Storage for a tab bar struct ImGuiTabItem; // Storage for a tab item (within a tab bar) struct ImGuiTable; // Storage for a table struct ImGuiTableColumn; // Storage for one column of a table +struct ImGuiTableTempData; // Temporary storage for one table (one per table in the stack), shared between tables. struct ImGuiTableSettings; // Storage for a table .ini settings struct ImGuiTableColumnsSettings; // Storage for a column .ini settings struct ImGuiWindow; // Storage for one window -struct ImGuiWindowTempData; // Temporary storage for one window (that's the data which in theory we could ditch at the end of the frame) +struct ImGuiWindowTempData; // Temporary storage for one window (that's the data which in theory we could ditch at the end of the frame, in practice we currently keep it for each window) struct ImGuiWindowSettings; // Storage for a window .ini settings (we keep one of those even if the actual window wasn't instanced during this session) // Use your programming IDE "Go to definition" facility on the names of the center columns to find the actual flags/enum lists. typedef int ImGuiLayoutType; // -> enum ImGuiLayoutType_ // Enum: Horizontal or vertical +typedef int ImGuiActivateFlags; // -> enum ImGuiActivateFlags_ // Flags: for navigation/focus function (will be for ActivateItem() later) typedef int ImGuiItemFlags; // -> enum ImGuiItemFlags_ // Flags: for PushItemFlag() typedef int ImGuiItemStatusFlags; // -> enum ImGuiItemStatusFlags_ // Flags: for DC.LastItemStatusFlags typedef int ImGuiOldColumnFlags; // -> enum ImGuiOldColumnFlags_ // Flags: for BeginColumns() @@ -129,6 +154,7 @@ typedef int ImGuiNavDirSourceFlags; // -> enum ImGuiNavDirSourceFlags_ // F typedef int ImGuiNavMoveFlags; // -> enum ImGuiNavMoveFlags_ // Flags: for navigation requests typedef int ImGuiNextItemDataFlags; // -> enum ImGuiNextItemDataFlags_ // Flags: for SetNextItemXXX() functions typedef int ImGuiNextWindowDataFlags; // -> enum ImGuiNextWindowDataFlags_// Flags: for SetNextWindowXXX() functions +typedef int ImGuiScrollFlags; // -> enum ImGuiScrollFlags_ // Flags: for ScrollToItem() and navigation requests typedef int ImGuiSeparatorFlags; // -> enum ImGuiSeparatorFlags_ // Flags: for SeparatorEx() typedef int ImGuiTextFlags; // -> enum ImGuiTextFlags_ // Flags: for TextEx() typedef int ImGuiTooltipFlags; // -> enum ImGuiTooltipFlags_ // Flags: for BeginTooltipEx() @@ -174,15 +200,13 @@ namespace ImStb // Debug Logging for selected systems. Remove the '((void)0) //' to enable. //#define IMGUI_DEBUG_LOG_POPUP IMGUI_DEBUG_LOG // Enable log //#define IMGUI_DEBUG_LOG_NAV IMGUI_DEBUG_LOG // Enable log +//#define IMGUI_DEBUG_LOG_IO IMGUI_DEBUG_LOG // Enable log #define IMGUI_DEBUG_LOG_POPUP(...) ((void)0) // Disable log #define IMGUI_DEBUG_LOG_NAV(...) ((void)0) // Disable log +#define IMGUI_DEBUG_LOG_IO(...) ((void)0) // Disable log // Static Asserts -#if (__cplusplus >= 201100) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201100) #define IM_STATIC_ASSERT(_COND) static_assert(_COND, "") -#else -#define IM_STATIC_ASSERT(_COND) typedef char static_assertion_##__line__[(_COND)?1:-1] -#endif // "Paranoid" Debug Asserts are meant to only be enabled during specific debugging/work, otherwise would slow down the code too much. // We currently don't have many of those so the effect is currently negligible, but onward intent to add more aggressive ones in the code. @@ -207,6 +231,7 @@ namespace ImStb #define IM_NEWLINE "\n" #endif #define IM_TABSIZE (4) +#define IM_MEMALIGN(_OFF,_ALIGN) (((_OFF) + (_ALIGN - 1)) & ~(_ALIGN - 1)) // Memory align e.g. IM_ALIGN(0,4)=0, IM_ALIGN(1,4)=4, IM_ALIGN(4,4)=4, IM_ALIGN(5,4)=8 #define IM_F32_TO_INT8_UNBOUND(_VAL) ((int)((_VAL) * 255.0f + ((_VAL)>=0 ? 0.5f : -0.5f))) // Unsaturated, for display purpose #define IM_F32_TO_INT8_SAT(_VAL) ((int)(ImSaturate(_VAL) * 255.0f + 0.5f)) // Saturated, always output 0..255 #define IM_FLOOR(_VAL) ((float)(int)(_VAL)) // ImFloor() is not inlined in MSVC debug builds @@ -219,13 +244,27 @@ namespace ImStb #define IMGUI_CDECL #endif +// Warnings +#if defined(_MSC_VER) && !defined(__clang__) +#define IM_MSVC_WARNING_SUPPRESS(XXXX) __pragma(warning(suppress: XXXX)) +#else +#define IM_MSVC_WARNING_SUPPRESS(XXXX) +#endif + // Debug Tools -// Use 'Metrics->Tools->Item Picker' to break into the call-stack of a specific item. +// Use 'Metrics/Debugger->Tools->Item Picker' to break into the call-stack of a specific item. +// This will call IM_DEBUG_BREAK() which you may redefine yourself. See https://github.com/scottt/debugbreak for more reference. #ifndef IM_DEBUG_BREAK -#if defined(__clang__) -#define IM_DEBUG_BREAK() __builtin_debugtrap() -#elif defined (_MSC_VER) +#if defined (_MSC_VER) #define IM_DEBUG_BREAK() __debugbreak() +#elif defined(__clang__) +#define IM_DEBUG_BREAK() __builtin_debugtrap() +#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) +#define IM_DEBUG_BREAK() __asm__ volatile("int $0x03") +#elif defined(__GNUC__) && defined(__thumb__) +#define IM_DEBUG_BREAK() __asm__ volatile(".inst 0xde01") +#elif defined(__GNUC__) && defined(__arm__) && !defined(__thumb__) +#define IM_DEBUG_BREAK() __asm__ volatile(".inst 0xe7f001f0"); #else #define IM_DEBUG_BREAK() IM_ASSERT(0) // It is expected that you define IM_DEBUG_BREAK() into something that will break nicely in a debugger! #endif @@ -262,7 +301,9 @@ static inline ImGuiID ImHash(const void* data, int size, ImU32 seed = 0) { ret #endif // Helpers: Sorting -#define ImQsort qsort +#ifndef ImQsort +static inline void ImQsort(void* base, size_t count, size_t size_of_element, int(IMGUI_CDECL *compare_func)(void const*, void const*)) { if (count > 1) qsort(base, count, size_of_element, compare_func); } +#endif // Helpers: Color Blending IMGUI_API ImU32 ImAlphaBlendColors(ImU32 col_a, ImU32 col_b); @@ -295,17 +336,19 @@ static inline bool ImCharIsBlankA(char c) { return c == ' ' || c = static inline bool ImCharIsBlankW(unsigned int c) { return c == ' ' || c == '\t' || c == 0x3000; } // Helpers: UTF-8 <> wchar conversions -IMGUI_API int ImTextStrToUtf8(char* buf, int buf_size, const ImWchar* in_text, const ImWchar* in_text_end); // return output UTF-8 bytes count -IMGUI_API int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end); // read one character. return input UTF-8 bytes count -IMGUI_API int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_remaining = NULL); // return input UTF-8 bytes count -IMGUI_API int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end); // return number of UTF-8 code-points (NOT bytes count) -IMGUI_API int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end); // return number of bytes to express one char in UTF-8 -IMGUI_API int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end); // return number of bytes to express string in UTF-8 +IMGUI_API const char* ImTextCharToUtf8(char out_buf[5], unsigned int c); // return out_buf +IMGUI_API int ImTextStrToUtf8(char* out_buf, int out_buf_size, const ImWchar* in_text, const ImWchar* in_text_end); // return output UTF-8 bytes count +IMGUI_API int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end); // read one character. return input UTF-8 bytes count +IMGUI_API int ImTextStrFromUtf8(ImWchar* out_buf, int out_buf_size, const char* in_text, const char* in_text_end, const char** in_remaining = NULL); // return input UTF-8 bytes count +IMGUI_API int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end); // return number of UTF-8 code-points (NOT bytes count) +IMGUI_API int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end); // return number of bytes to express one char in UTF-8 +IMGUI_API int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end); // return number of bytes to express string in UTF-8 // Helpers: ImVec2/ImVec4 operators // We are keeping those disabled by default so they don't leak in user space, to allow user enabling implicit cast operators between ImVec2 and their own types (using IM_VEC2_CLASS_EXTRA etc.) // We unfortunately don't have a unary- operator for ImVec2 because this would needs to be defined inside the class itself. #ifdef IMGUI_DEFINE_MATH_OPERATORS +IM_MSVC_RUNTIME_CHECKS_OFF static inline ImVec2 operator*(const ImVec2& lhs, const float rhs) { return ImVec2(lhs.x * rhs, lhs.y * rhs); } static inline ImVec2 operator/(const ImVec2& lhs, const float rhs) { return ImVec2(lhs.x / rhs, lhs.y / rhs); } static inline ImVec2 operator+(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x + rhs.x, lhs.y + rhs.y); } @@ -321,6 +364,7 @@ static inline ImVec2& operator/=(ImVec2& lhs, const ImVec2& rhs) static inline ImVec4 operator+(const ImVec4& lhs, const ImVec4& rhs) { return ImVec4(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z, lhs.w + rhs.w); } static inline ImVec4 operator-(const ImVec4& lhs, const ImVec4& rhs) { return ImVec4(lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z, lhs.w - rhs.w); } static inline ImVec4 operator*(const ImVec4& lhs, const ImVec4& rhs) { return ImVec4(lhs.x * rhs.x, lhs.y * rhs.y, lhs.z * rhs.z, lhs.w * rhs.w); } +IM_MSVC_RUNTIME_CHECKS_RESTORE #endif // Helpers: File System @@ -346,6 +390,7 @@ IMGUI_API ImU64 ImFileWrite(const void* data, ImU64 size, ImU64 coun IMGUI_API void* ImFileLoadToMemory(const char* filename, const char* mode, size_t* out_file_size = NULL, int padding_bytes = 0); // Helpers: Maths +IM_MSVC_RUNTIME_CHECKS_OFF // - Wrapper for standard libs functions. (Note that imgui_demo.cpp does _not_ use them to keep the code easy to copy) #ifndef IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS #define ImFabs(X) fabsf(X) @@ -356,16 +401,23 @@ IMGUI_API void* ImFileLoadToMemory(const char* filename, const char* #define ImAcos(X) acosf(X) #define ImAtan2(Y, X) atan2f((Y), (X)) #define ImAtof(STR) atof(STR) -#define ImFloorStd(X) floorf(X) // We already uses our own ImFloor() { return (float)(int)v } internally so the standard one wrapper is named differently (it's used by e.g. stb_truetype) +//#define ImFloorStd(X) floorf(X) // We use our own, see ImFloor() and ImFloorSigned() #define ImCeil(X) ceilf(X) static inline float ImPow(float x, float y) { return powf(x, y); } // DragBehaviorT/SliderBehaviorT uses ImPow with either float/double and need the precision static inline double ImPow(double x, double y) { return pow(x, y); } static inline float ImLog(float x) { return logf(x); } // DragBehaviorT/SliderBehaviorT uses ImLog with either float/double and need the precision static inline double ImLog(double x) { return log(x); } +static inline int ImAbs(int x) { return x < 0 ? -x : x; } static inline float ImAbs(float x) { return fabsf(x); } static inline double ImAbs(double x) { return fabs(x); } -static inline float ImSign(float x) { return (x < 0.0f) ? -1.0f : ((x > 0.0f) ? 1.0f : 0.0f); } // Sign operator - returns -1, 0 or 1 based on sign of argument -static inline double ImSign(double x) { return (x < 0.0) ? -1.0 : ((x > 0.0) ? 1.0 : 0.0); } +static inline float ImSign(float x) { return (x < 0.0f) ? -1.0f : (x > 0.0f) ? 1.0f : 0.0f; } // Sign operator - returns -1, 0 or 1 based on sign of argument +static inline double ImSign(double x) { return (x < 0.0) ? -1.0 : (x > 0.0) ? 1.0 : 0.0; } +#ifdef IMGUI_ENABLE_SSE +static inline float ImRsqrt(float x) { return _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ss(x))); } +#else +static inline float ImRsqrt(float x) { return 1.0f / sqrtf(x); } +#endif +static inline double ImRsqrt(double x) { return 1.0 / sqrt(x); } #endif // - ImMin/ImMax/ImClamp/ImLerp/ImSwap are used by widgets which support variety of types: signed/unsigned int/long long float/double // (Exceptionally using templates here but we could also redefine them for those types) @@ -386,14 +438,18 @@ static inline ImVec4 ImLerp(const ImVec4& a, const ImVec4& b, float t) static inline float ImSaturate(float f) { return (f < 0.0f) ? 0.0f : (f > 1.0f) ? 1.0f : f; } static inline float ImLengthSqr(const ImVec2& lhs) { return (lhs.x * lhs.x) + (lhs.y * lhs.y); } static inline float ImLengthSqr(const ImVec4& lhs) { return (lhs.x * lhs.x) + (lhs.y * lhs.y) + (lhs.z * lhs.z) + (lhs.w * lhs.w); } -static inline float ImInvLength(const ImVec2& lhs, float fail_value) { float d = (lhs.x * lhs.x) + (lhs.y * lhs.y); if (d > 0.0f) return 1.0f / ImSqrt(d); return fail_value; } +static inline float ImInvLength(const ImVec2& lhs, float fail_value) { float d = (lhs.x * lhs.x) + (lhs.y * lhs.y); if (d > 0.0f) return ImRsqrt(d); return fail_value; } static inline float ImFloor(float f) { return (float)(int)(f); } +static inline float ImFloorSigned(float f) { return (float)((f >= 0 || (float)(int)f == f) ? (int)f : (int)f - 1); } // Decent replacement for floorf() static inline ImVec2 ImFloor(const ImVec2& v) { return ImVec2((float)(int)(v.x), (float)(int)(v.y)); } +static inline ImVec2 ImFloorSigned(const ImVec2& v) { return ImVec2(ImFloorSigned(v.x), ImFloorSigned(v.y)); } static inline int ImModPositive(int a, int b) { return (a + b) % b; } static inline float ImDot(const ImVec2& a, const ImVec2& b) { return a.x * b.x + a.y * b.y; } static inline ImVec2 ImRotate(const ImVec2& v, float cos_a, float sin_a) { return ImVec2(v.x * cos_a - v.y * sin_a, v.x * sin_a + v.y * cos_a); } static inline float ImLinearSweep(float current, float target, float speed) { if (current < target) return ImMin(current + speed, target); if (current > target) return ImMax(current - speed, target); return current; } static inline ImVec2 ImMul(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x * rhs.x, lhs.y * rhs.y); } +static inline bool ImIsFloatAboveGuaranteedIntegerPrecision(float f) { return f <= -16777216 || f >= 16777216; } +IM_MSVC_RUNTIME_CHECKS_RESTORE // Helpers: Geometry IMGUI_API ImVec2 ImBezierCubicCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, float t); @@ -409,6 +465,7 @@ IMGUI_API ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy); // Helper: ImVec1 (1D vector) // (this odd construct is used to facilitate the transition between 1D and 2D, and the maintenance of some branches/patches) +IM_MSVC_RUNTIME_CHECKS_OFF struct ImVec1 { float x; @@ -441,6 +498,7 @@ struct IMGUI_API ImRect ImVec2 GetSize() const { return ImVec2(Max.x - Min.x, Max.y - Min.y); } float GetWidth() const { return Max.x - Min.x; } float GetHeight() const { return Max.y - Min.y; } + float GetArea() const { return (Max.x - Min.x) * (Max.y - Min.y); } ImVec2 GetTL() const { return Min; } // Top-left ImVec2 GetTR() const { return ImVec2(Max.x, Min.y); } // Top-right ImVec2 GetBL() const { return ImVec2(Min.x, Max.y); } // Bottom-left @@ -461,6 +519,7 @@ struct IMGUI_API ImRect bool IsInverted() const { return Min.x > Max.x || Min.y > Max.y; } ImVec4 ToVec4() const { return ImVec4(Min.x, Min.y, Max.x, Max.y); } }; +IM_MSVC_RUNTIME_CHECKS_RESTORE // Helper: ImBitArray inline bool ImBitArrayTestBit(const ImU32* arr, int n) { ImU32 mask = (ImU32)1 << (n & 31); return (arr[n >> 5] & mask) != 0; } @@ -480,18 +539,19 @@ inline void ImBitArraySetBitRange(ImU32* arr, int n, int n2) // Works on ran } // Helper: ImBitArray class (wrapper over ImBitArray functions) -// Store 1-bit per value. NOT CLEARED by constructor. -template -struct IMGUI_API ImBitArray +// Store 1-bit per value. +template +struct ImBitArray { ImU32 Storage[(BITCOUNT + 31) >> 5]; - ImBitArray() { } + ImBitArray() { ClearAllBits(); } void ClearAllBits() { memset(Storage, 0, sizeof(Storage)); } void SetAllBits() { memset(Storage, 255, sizeof(Storage)); } - bool TestBit(int n) const { IM_ASSERT(n < BITCOUNT); return ImBitArrayTestBit(Storage, n); } - void SetBit(int n) { IM_ASSERT(n < BITCOUNT); ImBitArraySetBit(Storage, n); } - void ClearBit(int n) { IM_ASSERT(n < BITCOUNT); ImBitArrayClearBit(Storage, n); } - void SetBitRange(int n, int n2) { ImBitArraySetBitRange(Storage, n, n2); } // Works on range [n..n2) + bool TestBit(int n) const { IM_ASSERT(n + OFFSET < BITCOUNT); return ImBitArrayTestBit(Storage, n + OFFSET); } + void SetBit(int n) { IM_ASSERT(n + OFFSET < BITCOUNT); ImBitArraySetBit(Storage, n + OFFSET); } + void ClearBit(int n) { IM_ASSERT(n + OFFSET < BITCOUNT); ImBitArrayClearBit(Storage, n + OFFSET); } + void SetBitRange(int n, int n2) { ImBitArraySetBitRange(Storage, n + OFFSET, n2 + OFFSET); } // Works on range [n..n2) + bool operator[](int n) const { IM_ASSERT(n + OFFSET < BITCOUNT); return ImBitArrayTestBit(Storage, n + OFFSET); } }; // Helper: ImBitVector @@ -537,20 +597,22 @@ struct ImSpan // Helper: ImSpanAllocator<> // Facilitate storing multiple chunks into a single large block (the "arena") +// - Usage: call Reserve() N times, allocate GetArenaSizeInBytes() worth, pass it to SetArenaBasePtr(), call GetSpan() N times to retrieve the aligned ranges. template struct ImSpanAllocator { char* BasePtr; - int TotalSize; - int CurrSpan; + int CurrOff; + int CurrIdx; int Offsets[CHUNKS]; + int Sizes[CHUNKS]; ImSpanAllocator() { memset(this, 0, sizeof(*this)); } - inline void ReserveBytes(int n, size_t sz) { IM_ASSERT(n == CurrSpan && n < CHUNKS); IM_UNUSED(n); Offsets[CurrSpan++] = TotalSize; TotalSize += (int)sz; } - inline int GetArenaSizeInBytes() { return TotalSize; } + inline void Reserve(int n, size_t sz, int a=4) { IM_ASSERT(n == CurrIdx && n < CHUNKS); CurrOff = IM_MEMALIGN(CurrOff, a); Offsets[n] = CurrOff; Sizes[n] = (int)sz; CurrIdx++; CurrOff += (int)sz; } + inline int GetArenaSizeInBytes() { return CurrOff; } inline void SetArenaBasePtr(void* base_ptr) { BasePtr = (char*)base_ptr; } - inline void* GetSpanPtrBegin(int n) { IM_ASSERT(n >= 0 && n < CHUNKS && CurrSpan == CHUNKS); return (void*)(BasePtr + Offsets[n]); } - inline void* GetSpanPtrEnd(int n) { IM_ASSERT(n >= 0 && n < CHUNKS && CurrSpan == CHUNKS); return (n + 1 < CHUNKS) ? BasePtr + Offsets[n + 1] : (void*)(BasePtr + TotalSize); } + inline void* GetSpanPtrBegin(int n) { IM_ASSERT(n >= 0 && n < CHUNKS && CurrIdx == CHUNKS); return (void*)(BasePtr + Offsets[n]); } + inline void* GetSpanPtrEnd(int n) { IM_ASSERT(n >= 0 && n < CHUNKS && CurrIdx == CHUNKS); return (void*)(BasePtr + Offsets[n] + Sizes[n]); } template inline void GetSpan(int n, ImSpan* span) { span->set((T*)GetSpanPtrBegin(n), (T*)GetSpanPtrEnd(n)); } }; @@ -560,41 +622,51 @@ struct ImSpanAllocator // Honor constructor/destructor. Add/remove invalidate all pointers. Indexes have the same lifetime as the associated object. typedef int ImPoolIdx; template -struct IMGUI_API ImPool +struct ImPool { ImVector Buf; // Contiguous data ImGuiStorage Map; // ID->Index ImPoolIdx FreeIdx; // Next free idx to use + ImPoolIdx AliveCount; // Number of active/alive items (for display purpose) - ImPool() { FreeIdx = 0; } + ImPool() { FreeIdx = AliveCount = 0; } ~ImPool() { Clear(); } T* GetByKey(ImGuiID key) { int idx = Map.GetInt(key, -1); return (idx != -1) ? &Buf[idx] : NULL; } T* GetByIndex(ImPoolIdx n) { return &Buf[n]; } ImPoolIdx GetIndex(const T* p) const { IM_ASSERT(p >= Buf.Data && p < Buf.Data + Buf.Size); return (ImPoolIdx)(p - Buf.Data); } T* GetOrAddByKey(ImGuiID key) { int* p_idx = Map.GetIntRef(key, -1); if (*p_idx != -1) return &Buf[*p_idx]; *p_idx = FreeIdx; return Add(); } bool Contains(const T* p) const { return (p >= Buf.Data && p < Buf.Data + Buf.Size); } - void Clear() { for (int n = 0; n < Map.Data.Size; n++) { int idx = Map.Data[n].val_i; if (idx != -1) Buf[idx].~T(); } Map.Clear(); Buf.clear(); FreeIdx = 0; } - T* Add() { int idx = FreeIdx; if (idx == Buf.Size) { Buf.resize(Buf.Size + 1); FreeIdx++; } else { FreeIdx = *(int*)&Buf[idx]; } IM_PLACEMENT_NEW(&Buf[idx]) T(); return &Buf[idx]; } + void Clear() { for (int n = 0; n < Map.Data.Size; n++) { int idx = Map.Data[n].val_i; if (idx != -1) Buf[idx].~T(); } Map.Clear(); Buf.clear(); FreeIdx = AliveCount = 0; } + T* Add() { int idx = FreeIdx; if (idx == Buf.Size) { Buf.resize(Buf.Size + 1); FreeIdx++; } else { FreeIdx = *(int*)&Buf[idx]; } IM_PLACEMENT_NEW(&Buf[idx]) T(); AliveCount++; return &Buf[idx]; } void Remove(ImGuiID key, const T* p) { Remove(key, GetIndex(p)); } - void Remove(ImGuiID key, ImPoolIdx idx) { Buf[idx].~T(); *(int*)&Buf[idx] = FreeIdx; FreeIdx = idx; Map.SetInt(key, -1); } + void Remove(ImGuiID key, ImPoolIdx idx) { Buf[idx].~T(); *(int*)&Buf[idx] = FreeIdx; FreeIdx = idx; Map.SetInt(key, -1); AliveCount--; } void Reserve(int capacity) { Buf.reserve(capacity); Map.Data.reserve(capacity); } - int GetSize() const { return Buf.Size; } + + // To iterate a ImPool: for (int n = 0; n < pool.GetMapSize(); n++) if (T* t = pool.TryGetMapData(n)) { ... } + // Can be avoided if you know .Remove() has never been called on the pool, or AliveCount == GetMapSize() + int GetAliveCount() const { return AliveCount; } // Number of active/alive items in the pool (for display purpose) + int GetBufSize() const { return Buf.Size; } + int GetMapSize() const { return Map.Data.Size; } // It is the map we need iterate to find valid items, since we don't have "alive" storage anywhere + T* TryGetMapData(ImPoolIdx n) { int idx = Map.Data[n].val_i; if (idx == -1) return NULL; return GetByIndex(idx); } +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + int GetSize() { return GetMapSize(); } // For ImPlot: should use GetMapSize() from (IMGUI_VERSION_NUM >= 18304) +#endif }; // Helper: ImChunkStream<> // Build and iterate a contiguous stream of variable-sized structures. // This is used by Settings to store persistent data while reducing allocation count. -// We store the chunk size first, and align the final size on 4 bytes boundaries (this what the '(X + 3) & ~3' statement is for) +// We store the chunk size first, and align the final size on 4 bytes boundaries. // The tedious/zealous amount of casting is to avoid -Wcast-align warnings. template -struct IMGUI_API ImChunkStream +struct ImChunkStream { ImVector Buf; void clear() { Buf.clear(); } bool empty() const { return Buf.Size == 0; } int size() const { return Buf.Size; } - T* alloc_chunk(size_t sz) { size_t HDR_SZ = 4; sz = ((HDR_SZ + sz) + 3u) & ~3u; int off = Buf.Size; Buf.resize(off + (int)sz); ((int*)(void*)(Buf.Data + off))[0] = (int)sz; return (T*)(void*)(Buf.Data + off + (int)HDR_SZ); } + T* alloc_chunk(size_t sz) { size_t HDR_SZ = 4; sz = IM_MEMALIGN(HDR_SZ + sz, 4u); int off = Buf.Size; Buf.resize(off + (int)sz); ((int*)(void*)(Buf.Data + off))[0] = (int)sz; return (T*)(void*)(Buf.Data + off + (int)HDR_SZ); } T* begin() { size_t HDR_SZ = 4; if (!Buf.Data) return NULL; return (T*)(void*)(Buf.Data + HDR_SZ); } T* next_chunk(T* p) { size_t HDR_SZ = 4; IM_ASSERT(p >= begin() && p < end()); p = (T*)(void*)((char*)(void*)p + chunk_size(p)); if (p == (T*)(void*)((char*)end() + HDR_SZ)) return (T*)0; IM_ASSERT(p < end()); return p; } int chunk_size(const T* p) { return ((const int*)p)[-1]; } @@ -610,14 +682,30 @@ struct IMGUI_API ImChunkStream //----------------------------------------------------------------------------- // ImDrawList: Helper function to calculate a circle's segment count given its radius and a "maximum error" value. -#define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MIN 12 +// Estimation of number of circle segment based on error is derived using method described in https://stackoverflow.com/a/2244088/15194693 +// Number of segments (N) is calculated using equation: +// N = ceil ( pi / acos(1 - error / r) ) where r > 0, error <= r +// Our equation is significantly simpler that one in the post thanks for choosing segment that is +// perpendicular to X axis. Follow steps in the article from this starting condition and you will +// will get this result. +// +// Rendering circles with an odd number of segments, while mathematically correct will produce +// asymmetrical results on the raster grid. Therefore we're rounding N to next even number (7->8, 8->8, 9->10 etc.) +// +#define IM_ROUNDUP_TO_EVEN(_V) ((((_V) + 1) / 2) * 2) +#define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MIN 4 #define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX 512 -#define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(_RAD,_MAXERROR) ImClamp((int)((IM_PI * 2.0f) / ImAcos(((_RAD) - (_MAXERROR)) / (_RAD))), IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MIN, IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX) +#define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(_RAD,_MAXERROR) ImClamp(IM_ROUNDUP_TO_EVEN((int)ImCeil(IM_PI / ImAcos(1 - ImMin((_MAXERROR), (_RAD)) / (_RAD)))), IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MIN, IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX) + +// Raw equation from IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC rewritten for 'r' and 'error'. +#define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_R(_N,_MAXERROR) ((_MAXERROR) / (1 - ImCos(IM_PI / ImMax((float)(_N), IM_PI)))) +#define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_ERROR(_N,_RAD) ((1 - ImCos(IM_PI / ImMax((float)(_N), IM_PI))) / (_RAD)) -// ImDrawList: You may set this to higher values (e.g. 2 or 3) to increase tessellation of fast rounded corners path. -#ifndef IM_DRAWLIST_ARCFAST_TESSELLATION_MULTIPLIER -#define IM_DRAWLIST_ARCFAST_TESSELLATION_MULTIPLIER 1 +// ImDrawList: Lookup table size for adaptive arc drawing, cover full circle. +#ifndef IM_DRAWLIST_ARCFAST_TABLE_SIZE +#define IM_DRAWLIST_ARCFAST_TABLE_SIZE 48 // Number of samples in lookup table. #endif +#define IM_DRAWLIST_ARCFAST_SAMPLE_MAX IM_DRAWLIST_ARCFAST_TABLE_SIZE // Sample index _PathArcToFastEx() for 360 angle. // Data shared between all ImDrawList instances // You may want to create your own instance of this if you want to use ImDrawList completely without ImGui. In that case, watch out for future changes to this structure. @@ -632,20 +720,22 @@ struct IMGUI_API ImDrawListSharedData ImDrawListFlags InitialFlags; // Initial flags at the beginning of the frame (it is possible to alter flags on a per-drawlist basis afterwards) // [Internal] Lookup tables - ImVec2 ArcFastVtx[12 * IM_DRAWLIST_ARCFAST_TESSELLATION_MULTIPLIER]; // FIXME: Bake rounded corners fill/borders in atlas - ImU8 CircleSegmentCounts[64]; // Precomputed segment count for given radius (array index + 1) before we calculate it dynamically (to avoid calculation overhead) + ImVec2 ArcFastVtx[IM_DRAWLIST_ARCFAST_TABLE_SIZE]; // Sample points on the quarter of the circle. + float ArcFastRadiusCutoff; // Cutoff radius after which arc drawing will fallback to slower PathArcTo() + ImU8 CircleSegmentCounts[64]; // Precomputed segment count for given radius before we calculate it dynamically (to avoid calculation overhead) const ImVec4* TexUvLines; // UV of anti-aliased lines in the atlas ImDrawListSharedData(); - void SetCircleSegmentMaxError(float max_error); + void SetCircleTessellationMaxError(float max_error); }; struct ImDrawDataBuilder { ImVector Layers[2]; // Global layers for: regular, tooltip - void Clear() { for (int n = 0; n < IM_ARRAYSIZE(Layers); n++) Layers[n].resize(0); } - void ClearFreeMemory() { for (int n = 0; n < IM_ARRAYSIZE(Layers); n++) Layers[n].clear(); } + void Clear() { for (int n = 0; n < IM_ARRAYSIZE(Layers); n++) Layers[n].resize(0); } + void ClearFreeMemory() { for (int n = 0; n < IM_ARRAYSIZE(Layers); n++) Layers[n].clear(); } + int GetDrawListCount() const { int count = 0; for (int n = 0; n < IM_ARRAYSIZE(Layers); n++) count += Layers[n].Size; return count; } IMGUI_API void FlattenIntoSingleLayer(); }; @@ -658,38 +748,49 @@ struct ImDrawDataBuilder enum ImGuiItemFlags_ { ImGuiItemFlags_None = 0, - ImGuiItemFlags_NoTabStop = 1 << 0, // false - ImGuiItemFlags_ButtonRepeat = 1 << 1, // false // Button() will return true multiple times based on io.KeyRepeatDelay and io.KeyRepeatRate settings. - ImGuiItemFlags_Disabled = 1 << 2, // false // [BETA] Disable interactions but doesn't affect visuals yet. See github.com/ocornut/imgui/issues/211 - ImGuiItemFlags_NoNav = 1 << 3, // false - ImGuiItemFlags_NoNavDefaultFocus = 1 << 4, // false - ImGuiItemFlags_SelectableDontClosePopup = 1 << 5, // false // MenuItem/Selectable() automatically closes current Popup window - ImGuiItemFlags_MixedValue = 1 << 6, // false // [BETA] Represent a mixed/indeterminate value, generally multi-selection where values differ. Currently only supported by Checkbox() (later should support all sorts of widgets) - ImGuiItemFlags_ReadOnly = 1 << 7, // false // [ALPHA] Allow hovering interactions but underlying value is not changed. - ImGuiItemFlags_Default_ = 0 + ImGuiItemFlags_NoTabStop = 1 << 0, // false // Disable keyboard tabbing (FIXME: should merge with _NoNav) + ImGuiItemFlags_ButtonRepeat = 1 << 1, // false // Button() will return true multiple times based on io.KeyRepeatDelay and io.KeyRepeatRate settings. + ImGuiItemFlags_Disabled = 1 << 2, // false // Disable interactions but doesn't affect visuals. See BeginDisabled()/EndDisabled(). See github.com/ocornut/imgui/issues/211 + ImGuiItemFlags_NoNav = 1 << 3, // false // Disable keyboard/gamepad directional navigation (FIXME: should merge with _NoTabStop) + ImGuiItemFlags_NoNavDefaultFocus = 1 << 4, // false // Disable item being a candidate for default focus (e.g. used by title bar items) + ImGuiItemFlags_SelectableDontClosePopup = 1 << 5, // false // Disable MenuItem/Selectable() automatically closing their popup window + ImGuiItemFlags_MixedValue = 1 << 6, // false // [BETA] Represent a mixed/indeterminate value, generally multi-selection where values differ. Currently only supported by Checkbox() (later should support all sorts of widgets) + ImGuiItemFlags_ReadOnly = 1 << 7, // false // [ALPHA] Allow hovering interactions but underlying value is not changed. + ImGuiItemFlags_Inputable = 1 << 8 // false // [WIP] Auto-activate input mode when tab focused. Currently only used and supported by a few items before it becomes a generic feature. }; // Storage for LastItem data enum ImGuiItemStatusFlags_ { ImGuiItemStatusFlags_None = 0, - ImGuiItemStatusFlags_HoveredRect = 1 << 0, - ImGuiItemStatusFlags_HasDisplayRect = 1 << 1, + ImGuiItemStatusFlags_HoveredRect = 1 << 0, // Mouse position is within item rectangle (does NOT mean that the window is in correct z-order and can be hovered!, this is only one part of the most-common IsItemHovered test) + ImGuiItemStatusFlags_HasDisplayRect = 1 << 1, // g.LastItemData.DisplayRect is valid ImGuiItemStatusFlags_Edited = 1 << 2, // Value exposed by item was edited in the current frame (should match the bool return value of most widgets) - ImGuiItemStatusFlags_ToggledSelection = 1 << 3, // Set when Selectable(), TreeNode() reports toggling a selection. We can't report "Selected" because reporting the change allows us to handle clipping with less issues. + ImGuiItemStatusFlags_ToggledSelection = 1 << 3, // Set when Selectable(), TreeNode() reports toggling a selection. We can't report "Selected", only state changes, in order to easily handle clipping with less issues. ImGuiItemStatusFlags_ToggledOpen = 1 << 4, // Set when TreeNode() reports toggling their open state. ImGuiItemStatusFlags_HasDeactivated = 1 << 5, // Set if the widget/group is able to provide data for the ImGuiItemStatusFlags_Deactivated flag. - ImGuiItemStatusFlags_Deactivated = 1 << 6 // Only valid if ImGuiItemStatusFlags_HasDeactivated is set. + ImGuiItemStatusFlags_Deactivated = 1 << 6, // Only valid if ImGuiItemStatusFlags_HasDeactivated is set. + ImGuiItemStatusFlags_HoveredWindow = 1 << 7, // Override the HoveredWindow test to allow cross-window hover testing. + ImGuiItemStatusFlags_FocusedByTabbing = 1 << 8 // Set when the Focusable item just got focused by Tabbing (FIXME: to be removed soon) #ifdef IMGUI_ENABLE_TEST_ENGINE , // [imgui_tests only] - ImGuiItemStatusFlags_Openable = 1 << 10, // - ImGuiItemStatusFlags_Opened = 1 << 11, // - ImGuiItemStatusFlags_Checkable = 1 << 12, // - ImGuiItemStatusFlags_Checked = 1 << 13 // + ImGuiItemStatusFlags_Openable = 1 << 20, // + ImGuiItemStatusFlags_Opened = 1 << 21, // + ImGuiItemStatusFlags_Checkable = 1 << 22, // + ImGuiItemStatusFlags_Checked = 1 << 23 // #endif }; +// Extend ImGuiInputTextFlags_ +enum ImGuiInputTextFlagsPrivate_ +{ + // [Internal] + ImGuiInputTextFlags_Multiline = 1 << 26, // For internal use by InputTextMultiline() + ImGuiInputTextFlags_NoMarkEdited = 1 << 27, // For internal use by functions using InputText() before reformatting data + ImGuiInputTextFlags_MergedItem = 1 << 28 // For internal use by TempInputText(), will skip calling ItemAdd(). Require bounding-box to strictly match. +}; + // Extend ImGuiButtonFlags_ enum ImGuiButtonFlagsPrivate_ { @@ -703,7 +804,7 @@ enum ImGuiButtonFlagsPrivate_ ImGuiButtonFlags_FlattenChildren = 1 << 11, // allow interactions even if a child window is overlapping ImGuiButtonFlags_AllowItemOverlap = 1 << 12, // require previous frame HoveredId to either match id or be null before being usable, use along with SetItemAllowOverlap() ImGuiButtonFlags_DontClosePopups = 1 << 13, // disable automatically closing parent popup on press // [UNUSED] - ImGuiButtonFlags_Disabled = 1 << 14, // disable interactions + //ImGuiButtonFlags_Disabled = 1 << 14, // disable interactions -> use BeginDisabled() or ImGuiItemFlags_Disabled ImGuiButtonFlags_AlignTextBaseLine = 1 << 15, // vertically align button to match text baseline - ButtonEx() only // FIXME: Should be removed and handled by SmallButton(), not possible currently because of DC.CursorPosPrevLine ImGuiButtonFlags_NoKeyModifiers = 1 << 16, // disable mouse interaction if a key modifier is held ImGuiButtonFlags_NoHoldingActiveId = 1 << 17, // don't set ActiveId while holding the mouse (ImGuiButtonFlags_PressedOnClick only) @@ -713,6 +814,12 @@ enum ImGuiButtonFlagsPrivate_ ImGuiButtonFlags_PressedOnDefault_ = ImGuiButtonFlags_PressedOnClickRelease }; +// Extend ImGuiComboFlags_ +enum ImGuiComboFlagsPrivate_ +{ + ImGuiComboFlags_CustomPreview = 1 << 20 // enable BeginComboPreview() +}; + // Extend ImGuiSliderFlags_ enum ImGuiSliderFlagsPrivate_ { @@ -725,12 +832,13 @@ enum ImGuiSelectableFlagsPrivate_ { // NB: need to be in sync with last value of ImGuiSelectableFlags_ ImGuiSelectableFlags_NoHoldingActiveID = 1 << 20, - ImGuiSelectableFlags_SelectOnClick = 1 << 21, // Override button behavior to react on Click (default is Click+Release) - ImGuiSelectableFlags_SelectOnRelease = 1 << 22, // Override button behavior to react on Release (default is Click+Release) - ImGuiSelectableFlags_SpanAvailWidth = 1 << 23, // Span all avail width even if we declared less for layout purpose. FIXME: We may be able to remove this (added in 6251d379, 2bcafc86 for menus) - ImGuiSelectableFlags_DrawHoveredWhenHeld = 1 << 24, // Always show active when held, even is not hovered. This concept could probably be renamed/formalized somehow. - ImGuiSelectableFlags_SetNavIdOnHover = 1 << 25, // Set Nav/Focus ID on mouse hover (used by MenuItem) - ImGuiSelectableFlags_NoPadWithHalfSpacing = 1 << 26 // Disable padding each side with ItemSpacing * 0.5f + ImGuiSelectableFlags_SelectOnNav = 1 << 21, // (WIP) Auto-select when moved into. This is not exposed in public API as to handle multi-select and modifiers we will need user to explicitly control focus scope. May be replaced with a BeginSelection() API. + ImGuiSelectableFlags_SelectOnClick = 1 << 22, // Override button behavior to react on Click (default is Click+Release) + ImGuiSelectableFlags_SelectOnRelease = 1 << 23, // Override button behavior to react on Release (default is Click+Release) + ImGuiSelectableFlags_SpanAvailWidth = 1 << 24, // Span all avail width even if we declared less for layout purpose. FIXME: We may be able to remove this (added in 6251d379, 2bcafc86 for menus) + ImGuiSelectableFlags_DrawHoveredWhenHeld = 1 << 25, // Always show active when held, even is not hovered. This concept could probably be renamed/formalized somehow. + ImGuiSelectableFlags_SetNavIdOnHover = 1 << 26, // Set Nav/Focus ID on mouse hover (used by MenuItem) + ImGuiSelectableFlags_NoPadWithHalfSpacing = 1 << 27 // Disable padding each side with ItemSpacing * 0.5f }; // Extend ImGuiTreeNodeFlags_ @@ -790,70 +898,6 @@ enum ImGuiPlotType ImGuiPlotType_Histogram }; -enum ImGuiInputSource -{ - ImGuiInputSource_None = 0, - ImGuiInputSource_Mouse, - ImGuiInputSource_Nav, - ImGuiInputSource_NavKeyboard, // Only used occasionally for storage, not tested/handled by most code - ImGuiInputSource_NavGamepad, // " - ImGuiInputSource_COUNT -}; - -// FIXME-NAV: Clarify/expose various repeat delay/rate -enum ImGuiInputReadMode -{ - ImGuiInputReadMode_Down, - ImGuiInputReadMode_Pressed, - ImGuiInputReadMode_Released, - ImGuiInputReadMode_Repeat, - ImGuiInputReadMode_RepeatSlow, - ImGuiInputReadMode_RepeatFast -}; - -enum ImGuiNavHighlightFlags_ -{ - ImGuiNavHighlightFlags_None = 0, - ImGuiNavHighlightFlags_TypeDefault = 1 << 0, - ImGuiNavHighlightFlags_TypeThin = 1 << 1, - ImGuiNavHighlightFlags_AlwaysDraw = 1 << 2, // Draw rectangular highlight if (g.NavId == id) _even_ when using the mouse. - ImGuiNavHighlightFlags_NoRounding = 1 << 3 -}; - -enum ImGuiNavDirSourceFlags_ -{ - ImGuiNavDirSourceFlags_None = 0, - ImGuiNavDirSourceFlags_Keyboard = 1 << 0, - ImGuiNavDirSourceFlags_PadDPad = 1 << 1, - ImGuiNavDirSourceFlags_PadLStick = 1 << 2 -}; - -enum ImGuiNavMoveFlags_ -{ - ImGuiNavMoveFlags_None = 0, - ImGuiNavMoveFlags_LoopX = 1 << 0, // On failed request, restart from opposite side - ImGuiNavMoveFlags_LoopY = 1 << 1, - ImGuiNavMoveFlags_WrapX = 1 << 2, // On failed request, request from opposite side one line down (when NavDir==right) or one line up (when NavDir==left) - ImGuiNavMoveFlags_WrapY = 1 << 3, // This is not super useful for provided for completeness - ImGuiNavMoveFlags_AllowCurrentNavId = 1 << 4, // Allow scoring and considering the current NavId as a move target candidate. This is used when the move source is offset (e.g. pressing PageDown actually needs to send a Up move request, if we are pressing PageDown from the bottom-most item we need to stay in place) - ImGuiNavMoveFlags_AlsoScoreVisibleSet = 1 << 5, // Store alternate result in NavMoveResultLocalVisibleSet that only comprise elements that are already fully visible. - ImGuiNavMoveFlags_ScrollToEdge = 1 << 6 -}; - -enum ImGuiNavForward -{ - ImGuiNavForward_None, - ImGuiNavForward_ForwardQueued, - ImGuiNavForward_ForwardActive -}; - -enum ImGuiNavLayer -{ - ImGuiNavLayer_Main = 0, // Main scrolling layer - ImGuiNavLayer_Menu = 1, // Menu layer (access with Alt/ImGuiNavInput_Menu) - ImGuiNavLayer_COUNT -}; - enum ImGuiPopupPositionPolicy { ImGuiPopupPositionPolicy_Default, @@ -900,8 +944,21 @@ struct ImGuiStyleMod ImGuiStyleMod(ImGuiStyleVar idx, ImVec2 v) { VarIdx = idx; BackupFloat[0] = v.x; BackupFloat[1] = v.y; } }; +// Storage data for BeginComboPreview()/EndComboPreview() +struct IMGUI_API ImGuiComboPreviewData +{ + ImRect PreviewRect; + ImVec2 BackupCursorPos; + ImVec2 BackupCursorMaxPos; + ImVec2 BackupCursorPosPrevLine; + float BackupPrevLineTextBaseOffset; + ImGuiLayoutType BackupLayout; + + ImGuiComboPreviewData() { memset(this, 0, sizeof(*this)); } +}; + // Stacked storage data for BeginGroup()/EndGroup() -struct ImGuiGroupData +struct IMGUI_API ImGuiGroupData { ImGuiID WindowID; ImVec2 BackupCursorPos; @@ -912,20 +969,26 @@ struct ImGuiGroupData float BackupCurrLineTextBaseOffset; ImGuiID BackupActiveIdIsAlive; bool BackupActiveIdPreviousFrameIsAlive; + bool BackupHoveredIdIsAlive; bool EmitItem; }; // Simple column measurement, currently used for MenuItem() only.. This is very short-sighted/throw-away code and NOT a generic helper. struct IMGUI_API ImGuiMenuColumns { - float Spacing; - float Width, NextWidth; - float Pos[3], NextWidths[3]; + ImU32 TotalWidth; + ImU32 NextTotalWidth; + ImU16 Spacing; + ImU16 OffsetIcon; // Always zero for now + ImU16 OffsetLabel; // Offsets are locked in Update() + ImU16 OffsetShortcut; + ImU16 OffsetMark; + ImU16 Widths[4]; // Width of: Icon, Label, Shortcut, Mark (accumulators for current frame) ImGuiMenuColumns() { memset(this, 0, sizeof(*this)); } - void Update(int count, float spacing, bool clear); - float DeclColumns(float w0, float w1, float w2); - float CalcExtraSpace(float avail_w) const; + void Update(float spacing, bool window_reappearing); + float DeclColumns(float w_icon, float w_label, float w_shortcut, float w_mark); + void CalcNextTotalWidth(bool update_offsets); }; // Internal state of the currently focused/edited text input box @@ -945,9 +1008,7 @@ struct IMGUI_API ImGuiInputTextState bool CursorFollow; // set when we want scrolling to follow the current cursor position (not always!) bool SelectedAllMouseLock; // after a double-click to select all, we ignore further mouse drags to update selection bool Edited; // edited this frame - ImGuiInputTextFlags UserFlags; // Temporarily set while we call user's callback - ImGuiInputTextCallback UserCallback; // " - void* UserCallbackData; // " + ImGuiInputTextFlags Flags; // copy of InputText() flags ImGuiInputTextState() { memset(this, 0, sizeof(*this)); } void ClearText() { CurLenW = CurLenA = 0; TextW[0] = 0; TextA[0] = 0; CursorClamp(); } @@ -961,6 +1022,9 @@ struct IMGUI_API ImGuiInputTextState void CursorClamp() { Stb.cursor = ImMin(Stb.cursor, CurLenW); Stb.select_start = ImMin(Stb.select_start, CurLenW); Stb.select_end = ImMin(Stb.select_end, CurLenW); } bool HasSelection() const { return Stb.select_start != Stb.select_end; } void ClearSelection() { Stb.select_start = Stb.select_end = Stb.cursor; } + int GetCursorPos() const { return Stb.cursor; } + int GetSelectionStart() const { return Stb.select_start; } + int GetSelectionEnd() const { return Stb.select_end; } void SelectAll() { Stb.select_start = 0; Stb.cursor = Stb.select_end = CurLenW; Stb.has_preferred_x = 0; } }; @@ -978,20 +1042,6 @@ struct ImGuiPopupData ImGuiPopupData() { memset(this, 0, sizeof(*this)); OpenFrameCount = -1; } }; -struct ImGuiNavMoveResult -{ - ImGuiWindow* Window; // Best candidate window - ImGuiID ID; // Best candidate ID - ImGuiID FocusScopeId; // Best candidate focus scope ID - float DistBox; // Best candidate box distance to current NavId - float DistCenter; // Best candidate center distance to current NavId - float DistAxial; - ImRect RectRel; // Best candidate bounding box in window relative space - - ImGuiNavMoveResult() { Clear(); } - void Clear() { Window = NULL; ID = FocusScopeId = 0; DistBox = DistCenter = DistAxial = FLT_MAX; RectRel = ImRect(); } -}; - enum ImGuiNextWindowDataFlags_ { ImGuiNextWindowDataFlags_None = 0, @@ -1022,7 +1072,7 @@ struct ImGuiNextWindowData ImGuiSizeCallback SizeCallback; void* SizeCallbackUserData; float BgAlphaVal; // Override background alpha - ImVec2 MenuBarOffsetMinVal; // *Always on* This is not exposed publicly, so we don't clear it. + ImVec2 MenuBarOffsetMinVal; // (Always on) This is not exposed publicly, so we don't clear it and it doesn't have a corresponding flag (could we? for consistency?) ImGuiNextWindowData() { memset(this, 0, sizeof(*this)); } inline void ClearFlags() { Flags = ImGuiNextWindowDataFlags_None; } @@ -1047,6 +1097,44 @@ struct ImGuiNextItemData inline void ClearFlags() { Flags = ImGuiNextItemDataFlags_None; } // Also cleared manually by ItemAdd()! }; +// Status storage for the last submitted item +struct ImGuiLastItemData +{ + ImGuiID ID; + ImGuiItemFlags InFlags; // See ImGuiItemFlags_ + ImGuiItemStatusFlags StatusFlags; // See ImGuiItemStatusFlags_ + ImRect Rect; // Full rectangle + ImRect NavRect; // Navigation scoring rectangle (not displayed) + ImRect DisplayRect; // Display rectangle (only if ImGuiItemStatusFlags_HasDisplayRect is set) + + ImGuiLastItemData() { memset(this, 0, sizeof(*this)); } +}; + +struct IMGUI_API ImGuiStackSizes +{ + short SizeOfIDStack; + short SizeOfColorStack; + short SizeOfStyleVarStack; + short SizeOfFontStack; + short SizeOfFocusScopeStack; + short SizeOfGroupStack; + short SizeOfItemFlagsStack; + short SizeOfBeginPopupStack; + short SizeOfDisabledStack; + + ImGuiStackSizes() { memset(this, 0, sizeof(*this)); } + void SetToCurrentState(); + void CompareWithCurrentState(); +}; + +// Data saved for each window pushed into the stack +struct ImGuiWindowStackData +{ + ImGuiWindow* Window; + ImGuiLastItemData ParentLastItemDataBackup; + ImGuiStackSizes StackSizesOnBegin; // Store size of various stacks for asserting +}; + struct ImGuiShrinkWidthItem { int Index; @@ -1062,6 +1150,195 @@ struct ImGuiPtrOrIndex ImGuiPtrOrIndex(int index) { Ptr = NULL; Index = index; } }; +//----------------------------------------------------------------------------- +// [SECTION] Inputs support +//----------------------------------------------------------------------------- + +typedef ImBitArray ImBitArrayForNamedKeys; + +enum ImGuiKeyPrivate_ +{ + ImGuiKey_LegacyNativeKey_BEGIN = 0, + ImGuiKey_LegacyNativeKey_END = 512, + ImGuiKey_Gamepad_BEGIN = ImGuiKey_GamepadStart, + ImGuiKey_Gamepad_END = ImGuiKey_GamepadRStickRight + 1 +}; + +enum ImGuiInputEventType +{ + ImGuiInputEventType_None = 0, + ImGuiInputEventType_MousePos, + ImGuiInputEventType_MouseWheel, + ImGuiInputEventType_MouseButton, + ImGuiInputEventType_Key, + ImGuiInputEventType_Char, + ImGuiInputEventType_Focus, + ImGuiInputEventType_COUNT +}; + +enum ImGuiInputSource +{ + ImGuiInputSource_None = 0, + ImGuiInputSource_Mouse, + ImGuiInputSource_Keyboard, + ImGuiInputSource_Gamepad, + ImGuiInputSource_Clipboard, // Currently only used by InputText() + ImGuiInputSource_Nav, // Stored in g.ActiveIdSource only + ImGuiInputSource_COUNT +}; + +// FIXME: Structures in the union below need to be declared as anonymous unions appears to be an extension? +// Using ImVec2() would fail on Clang 'union member 'MousePos' has a non-trivial default constructor' +struct ImGuiInputEventMousePos { float PosX, PosY; }; +struct ImGuiInputEventMouseWheel { float WheelX, WheelY; }; +struct ImGuiInputEventMouseButton { int Button; bool Down; }; +struct ImGuiInputEventKey { ImGuiKey Key; bool Down; float AnalogValue; }; +struct ImGuiInputEventText { unsigned int Char; }; +struct ImGuiInputEventAppFocused { bool Focused; }; + +struct ImGuiInputEvent +{ + ImGuiInputEventType Type; + ImGuiInputSource Source; + union + { + ImGuiInputEventMousePos MousePos; // if Type == ImGuiInputEventType_MousePos + ImGuiInputEventMouseWheel MouseWheel; // if Type == ImGuiInputEventType_MouseWheel + ImGuiInputEventMouseButton MouseButton; // if Type == ImGuiInputEventType_MouseButton + ImGuiInputEventKey Key; // if Type == ImGuiInputEventType_Key + ImGuiInputEventText Text; // if Type == ImGuiInputEventType_Text + ImGuiInputEventAppFocused AppFocused; // if Type == ImGuiInputEventType_Focus + }; + bool AddedByTestEngine; + + ImGuiInputEvent() { memset(this, 0, sizeof(*this)); } +}; + +// FIXME-NAV: Clarify/expose various repeat delay/rate +enum ImGuiInputReadMode +{ + ImGuiInputReadMode_Down, + ImGuiInputReadMode_Pressed, + ImGuiInputReadMode_Released, + ImGuiInputReadMode_Repeat, + ImGuiInputReadMode_RepeatSlow, + ImGuiInputReadMode_RepeatFast +}; + +//----------------------------------------------------------------------------- +// [SECTION] Clipper support +//----------------------------------------------------------------------------- + +struct ImGuiListClipperRange +{ + int Min; + int Max; + bool PosToIndexConvert; // Begin/End are absolute position (will be converted to indices later) + ImS8 PosToIndexOffsetMin; // Add to Min after converting to indices + ImS8 PosToIndexOffsetMax; // Add to Min after converting to indices + + static ImGuiListClipperRange FromIndices(int min, int max) { ImGuiListClipperRange r = { min, max, false, 0, 0 }; return r; } + static ImGuiListClipperRange FromPositions(float y1, float y2, int off_min, int off_max) { ImGuiListClipperRange r = { (int)y1, (int)y2, true, (ImS8)off_min, (ImS8)off_max }; return r; } +}; + +// Temporary clipper data, buffers shared/reused between instances +struct ImGuiListClipperData +{ + ImGuiListClipper* ListClipper; + float LossynessOffset; + int StepNo; + int ItemsFrozen; + ImVector Ranges; + + ImGuiListClipperData() { memset(this, 0, sizeof(*this)); } + void Reset(ImGuiListClipper* clipper) { ListClipper = clipper; StepNo = ItemsFrozen = 0; Ranges.resize(0); } +}; + +//----------------------------------------------------------------------------- +// [SECTION] Navigation support +//----------------------------------------------------------------------------- + +enum ImGuiActivateFlags_ +{ + ImGuiActivateFlags_None = 0, + ImGuiActivateFlags_PreferInput = 1 << 0, // Favor activation that requires keyboard text input (e.g. for Slider/Drag). Default if keyboard is available. + ImGuiActivateFlags_PreferTweak = 1 << 1, // Favor activation for tweaking with arrows or gamepad (e.g. for Slider/Drag). Default if keyboard is not available. + ImGuiActivateFlags_TryToPreserveState = 1 << 2 // Request widget to preserve state if it can (e.g. InputText will try to preserve cursor/selection) +}; + +// Early work-in-progress API for ScrollToItem() +enum ImGuiScrollFlags_ +{ + ImGuiScrollFlags_None = 0, + ImGuiScrollFlags_KeepVisibleEdgeX = 1 << 0, // If item is not visible: scroll as little as possible on X axis to bring item back into view [default for X axis] + ImGuiScrollFlags_KeepVisibleEdgeY = 1 << 1, // If item is not visible: scroll as little as possible on Y axis to bring item back into view [default for Y axis for windows that are already visible] + ImGuiScrollFlags_KeepVisibleCenterX = 1 << 2, // If item is not visible: scroll to make the item centered on X axis [rarely used] + ImGuiScrollFlags_KeepVisibleCenterY = 1 << 3, // If item is not visible: scroll to make the item centered on Y axis + ImGuiScrollFlags_AlwaysCenterX = 1 << 4, // Always center the result item on X axis [rarely used] + ImGuiScrollFlags_AlwaysCenterY = 1 << 5, // Always center the result item on Y axis [default for Y axis for appearing window) + ImGuiScrollFlags_NoScrollParent = 1 << 6, // Disable forwarding scrolling to parent window if required to keep item/rect visible (only scroll window the function was applied to). + ImGuiScrollFlags_MaskX_ = ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleCenterX | ImGuiScrollFlags_AlwaysCenterX, + ImGuiScrollFlags_MaskY_ = ImGuiScrollFlags_KeepVisibleEdgeY | ImGuiScrollFlags_KeepVisibleCenterY | ImGuiScrollFlags_AlwaysCenterY +}; + +enum ImGuiNavHighlightFlags_ +{ + ImGuiNavHighlightFlags_None = 0, + ImGuiNavHighlightFlags_TypeDefault = 1 << 0, + ImGuiNavHighlightFlags_TypeThin = 1 << 1, + ImGuiNavHighlightFlags_AlwaysDraw = 1 << 2, // Draw rectangular highlight if (g.NavId == id) _even_ when using the mouse. + ImGuiNavHighlightFlags_NoRounding = 1 << 3 +}; + +enum ImGuiNavDirSourceFlags_ +{ + ImGuiNavDirSourceFlags_None = 0, + ImGuiNavDirSourceFlags_RawKeyboard = 1 << 0, // Raw keyboard (not pulled from nav), faciliate use of some functions before we can unify nav and keys + ImGuiNavDirSourceFlags_Keyboard = 1 << 1, + ImGuiNavDirSourceFlags_PadDPad = 1 << 2, + ImGuiNavDirSourceFlags_PadLStick = 1 << 3 +}; + +enum ImGuiNavMoveFlags_ +{ + ImGuiNavMoveFlags_None = 0, + ImGuiNavMoveFlags_LoopX = 1 << 0, // On failed request, restart from opposite side + ImGuiNavMoveFlags_LoopY = 1 << 1, + ImGuiNavMoveFlags_WrapX = 1 << 2, // On failed request, request from opposite side one line down (when NavDir==right) or one line up (when NavDir==left) + ImGuiNavMoveFlags_WrapY = 1 << 3, // This is not super useful but provided for completeness + ImGuiNavMoveFlags_AllowCurrentNavId = 1 << 4, // Allow scoring and considering the current NavId as a move target candidate. This is used when the move source is offset (e.g. pressing PageDown actually needs to send a Up move request, if we are pressing PageDown from the bottom-most item we need to stay in place) + ImGuiNavMoveFlags_AlsoScoreVisibleSet = 1 << 5, // Store alternate result in NavMoveResultLocalVisible that only comprise elements that are already fully visible (used by PageUp/PageDown) + ImGuiNavMoveFlags_ScrollToEdgeY = 1 << 6, // Force scrolling to min/max (used by Home/End) // FIXME-NAV: Aim to remove or reword, probably unnecessary + ImGuiNavMoveFlags_Forwarded = 1 << 7, + ImGuiNavMoveFlags_DebugNoResult = 1 << 8, // Dummy scoring for debug purpose, don't apply result + ImGuiNavMoveFlags_FocusApi = 1 << 9, + ImGuiNavMoveFlags_Tabbing = 1 << 10, // == Focus + Activate if item is Inputable + DontChangeNavHighlight + ImGuiNavMoveFlags_Activate = 1 << 11, + ImGuiNavMoveFlags_DontSetNavHighlight = 1 << 12 // Do not alter the visible state of keyboard vs mouse nav highlight +}; + +enum ImGuiNavLayer +{ + ImGuiNavLayer_Main = 0, // Main scrolling layer + ImGuiNavLayer_Menu = 1, // Menu layer (access with Alt/ImGuiNavInput_Menu) + ImGuiNavLayer_COUNT +}; + +struct ImGuiNavItemData +{ + ImGuiWindow* Window; // Init,Move // Best candidate window (result->ItemWindow->RootWindowForNav == request->Window) + ImGuiID ID; // Init,Move // Best candidate item ID + ImGuiID FocusScopeId; // Init,Move // Best candidate focus scope ID + ImRect RectRel; // Init,Move // Best candidate bounding box in window relative space + ImGuiItemFlags InFlags; // ????,Move // Best candidate item flags + float DistBox; // Move // Best candidate box distance to current NavId + float DistCenter; // Move // Best candidate center distance to current NavId + float DistAxial; // Move // Best candidate axial distance to current NavId + + ImGuiNavItemData() { Clear(); } + void Clear() { Window = NULL; ID = FocusScopeId = 0; InFlags = 0; DistBox = DistCenter = DistAxial = FLT_MAX; } +}; + //----------------------------------------------------------------------------- // [SECTION] Columns support //----------------------------------------------------------------------------- @@ -1138,9 +1415,33 @@ struct ImGuiOldColumns // [SECTION] Viewport support //----------------------------------------------------------------------------- -#ifdef IMGUI_HAS_VIEWPORT -// -#endif // #ifdef IMGUI_HAS_VIEWPORT +// ImGuiViewport Private/Internals fields (cardinal sin: we are using inheritance!) +// Every instance of ImGuiViewport is in fact a ImGuiViewportP. +struct ImGuiViewportP : public ImGuiViewport +{ + int DrawListsLastFrame[2]; // Last frame number the background (0) and foreground (1) draw lists were used + ImDrawList* DrawLists[2]; // Convenience background (0) and foreground (1) draw lists. We use them to draw software mouser cursor when io.MouseDrawCursor is set and to draw most debug overlays. + ImDrawData DrawDataP; + ImDrawDataBuilder DrawDataBuilder; + + ImVec2 WorkOffsetMin; // Work Area: Offset from Pos to top-left corner of Work Area. Generally (0,0) or (0,+main_menu_bar_height). Work Area is Full Area but without menu-bars/status-bars (so WorkArea always fit inside Pos/Size!) + ImVec2 WorkOffsetMax; // Work Area: Offset from Pos+Size to bottom-right corner of Work Area. Generally (0,0) or (0,-status_bar_height). + ImVec2 BuildWorkOffsetMin; // Work Area: Offset being built during current frame. Generally >= 0.0f. + ImVec2 BuildWorkOffsetMax; // Work Area: Offset being built during current frame. Generally <= 0.0f. + + ImGuiViewportP() { DrawListsLastFrame[0] = DrawListsLastFrame[1] = -1; DrawLists[0] = DrawLists[1] = NULL; } + ~ImGuiViewportP() { if (DrawLists[0]) IM_DELETE(DrawLists[0]); if (DrawLists[1]) IM_DELETE(DrawLists[1]); } + + // Calculate work rect pos/size given a set of offset (we have 1 pair of offset for rect locked from last frame data, and 1 pair for currently building rect) + ImVec2 CalcWorkRectPos(const ImVec2& off_min) const { return ImVec2(Pos.x + off_min.x, Pos.y + off_min.y); } + ImVec2 CalcWorkRectSize(const ImVec2& off_min, const ImVec2& off_max) const { return ImVec2(ImMax(0.0f, Size.x - off_min.x + off_max.x), ImMax(0.0f, Size.y - off_min.y + off_max.y)); } + void UpdateWorkRect() { WorkPos = CalcWorkRectPos(WorkOffsetMin); WorkSize = CalcWorkRectSize(WorkOffsetMin, WorkOffsetMax); } // Update public fields + + // Helpers to retrieve ImRect (we don't need to store BuildWorkRect as every access tend to change it, hence the code asymmetry) + ImRect GetMainRect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } + ImRect GetWorkRect() const { return ImRect(WorkPos.x, WorkPos.y, WorkPos.x + WorkSize.x, WorkPos.y + WorkSize.y); } + ImRect GetBuildWorkRect() const { ImVec2 pos = CalcWorkRectPos(BuildWorkOffsetMin); ImVec2 size = CalcWorkRectSize(BuildWorkOffsetMin, BuildWorkOffsetMax); return ImRect(pos.x, pos.y, pos.x + size.x, pos.y + size.y); } +}; //----------------------------------------------------------------------------- // [SECTION] Settings support @@ -1177,11 +1478,12 @@ struct ImGuiSettingsHandler }; //----------------------------------------------------------------------------- -// [SECTION] Metrics, Debug +// [SECTION] Metrics, Debug Tools //----------------------------------------------------------------------------- struct ImGuiMetricsConfig { + bool ShowStackTool; bool ShowWindowsRects; bool ShowWindowsBeginOrder; bool ShowTablesRects; @@ -1192,6 +1494,7 @@ struct ImGuiMetricsConfig ImGuiMetricsConfig() { + ShowStackTool = false; ShowWindowsRects = false; ShowWindowsBeginOrder = false; ShowTablesRects = false; @@ -1202,19 +1505,25 @@ struct ImGuiMetricsConfig } }; -struct IMGUI_API ImGuiStackSizes +struct ImGuiStackLevelInfo { - short SizeOfIDStack; - short SizeOfColorStack; - short SizeOfStyleVarStack; - short SizeOfFontStack; - short SizeOfFocusScopeStack; - short SizeOfGroupStack; - short SizeOfBeginPopupStack; + ImGuiID ID; + ImS8 QueryFrameCount; // >= 1: Query in progress + bool QuerySuccess; // Obtained result from DebugHookIdInfo() + char Desc[58]; // Arbitrarily sized buffer to hold a result (FIXME: could replace Results[] with a chunk stream?) - ImGuiStackSizes() { memset(this, 0, sizeof(*this)); } - void SetToCurrentState(); - void CompareWithCurrentState(); + ImGuiStackLevelInfo() { memset(this, 0, sizeof(*this)); } +}; + +// State for Stack tool queries +struct ImGuiStackTool +{ + int LastActiveFrame; + int StackLevel; // -1: query stack and resize Results, >= 0: individual stack level + ImGuiID QueryId; // ID to query details for + ImVector Results; + + ImGuiStackTool() { memset(this, 0, sizeof(*this)); } }; //----------------------------------------------------------------------------- @@ -1222,10 +1531,11 @@ struct IMGUI_API ImGuiStackSizes //----------------------------------------------------------------------------- typedef void (*ImGuiContextHookCallback)(ImGuiContext* ctx, ImGuiContextHook* hook); -enum ImGuiContextHookType { ImGuiContextHookType_NewFramePre, ImGuiContextHookType_NewFramePost, ImGuiContextHookType_EndFramePre, ImGuiContextHookType_EndFramePost, ImGuiContextHookType_RenderPre, ImGuiContextHookType_RenderPost, ImGuiContextHookType_Shutdown }; +enum ImGuiContextHookType { ImGuiContextHookType_NewFramePre, ImGuiContextHookType_NewFramePost, ImGuiContextHookType_EndFramePre, ImGuiContextHookType_EndFramePost, ImGuiContextHookType_RenderPre, ImGuiContextHookType_RenderPost, ImGuiContextHookType_Shutdown, ImGuiContextHookType_PendingRemoval_ }; struct ImGuiContextHook { + ImGuiID HookId; // A unique ID assigned by AddContextHook() ImGuiContextHookType Type; ImGuiID Owner; ImGuiContextHookCallback Callback; @@ -1243,6 +1553,8 @@ struct ImGuiContext bool Initialized; bool FontAtlasOwnedByContext; // IO.Fonts-> is owned by the ImGuiContext and will be destructed along with it. ImGuiIO IO; + ImVector InputEventsQueue; // Input events which will be tricked/written into IO structure. + ImVector InputEventsTrail; // Past input events processed in NewFrame(). This is to allow domain-specific application to access e.g mouse/pen trail. ImGuiStyle Style; ImFont* Font; // (Shortcut) == FontStack.empty() ? IO.Font : FontStack.back() float FontSize; // (Shortcut) == FontBaseSize * g.CurrentWindow->FontWindowScale == window->FontSize(). Text height for current window. @@ -1257,19 +1569,18 @@ struct ImGuiContext bool WithinEndChild; // Set within EndChild() bool GcCompactAll; // Request full GC bool TestEngineHookItems; // Will call test engine hooks: ImGuiTestEngineHook_ItemAdd(), ImGuiTestEngineHook_ItemInfo(), ImGuiTestEngineHook_Log() - ImGuiID TestEngineHookIdInfo; // Will call test engine hooks: ImGuiTestEngineHook_IdInfo() from GetID() void* TestEngine; // Test engine user data // Windows state ImVector Windows; // Windows, sorted in display order, back to front - ImVector WindowsFocusOrder; // Windows, sorted in focus order, back to front. (FIXME: We could only store root windows here! Need to sort out the Docking equivalent which is RootWindowDockStop and is unfortunately a little more dynamic) + ImVector WindowsFocusOrder; // Root windows, sorted in focus order, back to front. ImVector WindowsTempSortBuffer; // Temporary buffer used in EndFrame() to reorder windows so parents are kept before their child - ImVector CurrentWindowStack; + ImVector CurrentWindowStack; ImGuiStorage WindowsById; // Map window's ImGuiID to ImGuiWindow* int WindowsActiveCount; // Number of unique windows submitted by frame + ImVec2 WindowsHoverPadding; // Padding around resizable windows for which hovering on counts as hovering the window == ImMax(style.TouchExtraPadding, WINDOWS_HOVER_PADDING) ImGuiWindow* CurrentWindow; // Window being drawn into ImGuiWindow* HoveredWindow; // Window the mouse is hovering. Will typically catch mouse inputs. - ImGuiWindow* HoveredRootWindow; // == HoveredWindow ? HoveredWindow->RootWindow : NULL, merely a shortcut to avoid null test in some situation. ImGuiWindow* HoveredWindowUnderMovingWindow; // Hovered window ignoring MovingWindow. Only set if MovingWindow is set. ImGuiWindow* MovingWindow; // Track the window we clicked on (in order to preserve focus). The actual window that is moved is generally MovingWindow->RootWindow. ImGuiWindow* WheelingWindow; // Track the window we started mouse-wheeling on. Until a timer elapse or mouse has moved, generally keep scrolling the same window even if during the course of scrolling the mouse ends up hovering a child window. @@ -1277,6 +1588,7 @@ struct ImGuiContext float WheelingWindowTimer; // Item/widgets state and tracking information + ImGuiID DebugHookIdInfo; // Will call core hooks: DebugHookIdInfo() from GetID functions, used by Stack Tool [next HoveredId/ActiveId to not pull in an extra cache-line] ImGuiID HoveredId; // Hovered widget, filled during the frame ImGuiID HoveredIdPreviousFrame; bool HoveredIdAllowOverlap; @@ -1297,7 +1609,7 @@ struct ImGuiContext bool ActiveIdUsingMouseWheel; // Active widget will want to read mouse wheel. Blocks scrolling the underlying window. ImU32 ActiveIdUsingNavDirMask; // Active widget will want to read those nav move requests (e.g. can activate a button and move away from it) ImU32 ActiveIdUsingNavInputMask; // Active widget will want to read those nav inputs. - ImU64 ActiveIdUsingKeyInputMask; // Active widget will want to read those key inputs. When we grow the ImGuiKey enum we'll need to either to order the enum to make useful keys come first, either redesign this into e.g. a small array. + ImBitArrayForNamedKeys ActiveIdUsingKeyInputMask; // Active widget will want to read those key inputs. When we grow the ImGuiKey enum we'll need to either to order the enum to make useful keys come first, either redesign this into e.g. a small array. ImVec2 ActiveIdClickOffset; // Clicked offset from upper-left corner, if applicable (currently only set by ButtonBehavior) ImGuiWindow* ActiveIdWindow; ImGuiInputSource ActiveIdSource; // Activating with mouse or nav (gamepad/keyboard) @@ -1310,8 +1622,10 @@ struct ImGuiContext float LastActiveIdTimer; // Store the last non-zero ActiveId timer since the beginning of activation, useful for animation. // Next window/item data - ImGuiNextWindowData NextWindowData; // Storage for SetNextWindow** functions + ImGuiItemFlags CurrentItemFlags; // == g.ItemFlagsStack.back() ImGuiNextItemData NextItemData; // Storage for SetNextItem** functions + ImGuiLastItemData LastItemData; // Storage for last submitted item (setup by ItemAdd) + ImGuiNextWindowData NextWindowData; // Storage for SetNextWindow** functions // Shared stacks ImVector ColorStack; // Stack for PushStyleColor()/PopStyleColor() - inherited by Begin() @@ -1322,6 +1636,10 @@ struct ImGuiContext ImVectorGroupStack; // Stack for BeginGroup()/EndGroup() - not inherited by Begin() ImVectorOpenPopupStack; // Which popups are open (persistent) ImVectorBeginPopupStack; // Which level of BeginPopup() we are in (reset every frame) + int BeginMenuCount; + + // Viewports + ImVector Viewports; // Active viewports (Size==1 in 'master' branch). Each viewports hold their copy of ImDrawData. // Gamepad/keyboard Navigation ImGuiWindow* NavWindow; // Focused window for navigation. Could be called 'FocusWindow' @@ -1330,37 +1648,44 @@ struct ImGuiContext ImGuiID NavActivateId; // ~~ (g.ActiveId == 0) && IsNavInputPressed(ImGuiNavInput_Activate) ? NavId : 0, also set when calling ActivateItem() ImGuiID NavActivateDownId; // ~~ IsNavInputDown(ImGuiNavInput_Activate) ? NavId : 0 ImGuiID NavActivatePressedId; // ~~ IsNavInputPressed(ImGuiNavInput_Activate) ? NavId : 0 - ImGuiID NavInputId; // ~~ IsNavInputPressed(ImGuiNavInput_Input) ? NavId : 0 - ImGuiID NavJustTabbedId; // Just tabbed to this id. + ImGuiID NavActivateInputId; // ~~ IsNavInputPressed(ImGuiNavInput_Input) ? NavId : 0; ImGuiActivateFlags_PreferInput will be set and NavActivateId will be 0. + ImGuiActivateFlags NavActivateFlags; ImGuiID NavJustMovedToId; // Just navigated to this id (result of a successfully MoveRequest). ImGuiID NavJustMovedToFocusScopeId; // Just navigated to this focus scope id (result of a successfully MoveRequest). ImGuiKeyModFlags NavJustMovedToKeyMods; ImGuiID NavNextActivateId; // Set by ActivateItem(), queued until next frame. + ImGuiActivateFlags NavNextActivateFlags; ImGuiInputSource NavInputSource; // Keyboard or Gamepad mode? THIS WILL ONLY BE None or NavGamepad or NavKeyboard. - ImRect NavScoringRect; // Rectangle used for scoring, in screen space. Based of window->NavRectRel[], modified for directional navigation scoring. - int NavScoringCount; // Metrics for debugging ImGuiNavLayer NavLayer; // Layer we are navigating on. For now the system is hard-coded for 0=main contents and 1=menu/title bar, may expose layers later. - int NavIdTabCounter; // == NavWindow->DC.FocusIdxTabCounter at time of NavId processing bool NavIdIsAlive; // Nav widget has been seen this frame ~~ NavRectRel is valid bool NavMousePosDirty; // When set we will update mouse position if (io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) if set (NB: this not enabled by default) bool NavDisableHighlight; // When user starts using mouse, we hide gamepad/keyboard highlight (NB: but they are still available, which is why NavDisableHighlight isn't always != NavDisableMouseHover) bool NavDisableMouseHover; // When user starts using gamepad/keyboard, we hide mouse hovering highlight until mouse is touched again. - bool NavAnyRequest; // ~~ NavMoveRequest || NavInitRequest + + // Navigation: Init & Move Requests + bool NavAnyRequest; // ~~ NavMoveRequest || NavInitRequest this is to perform early out in ItemAdd() bool NavInitRequest; // Init request for appearing window to select first item bool NavInitRequestFromMove; ImGuiID NavInitResultId; // Init request result (first item of the window, or one for which SetItemDefaultFocus() was called) ImRect NavInitResultRectRel; // Init request result rectangle (relative to parent window) - bool NavMoveRequest; // Move request for this frame - ImGuiNavMoveFlags NavMoveRequestFlags; - ImGuiNavForward NavMoveRequestForward; // None / ForwardQueued / ForwardActive (this is used to navigate sibling parent menus from a child menu) - ImGuiKeyModFlags NavMoveRequestKeyMods; - ImGuiDir NavMoveDir, NavMoveDirLast; // Direction of the move request (left/right/up/down), direction of the previous move request + bool NavMoveSubmitted; // Move request submitted, will process result on next NewFrame() + bool NavMoveScoringItems; // Move request submitted, still scoring incoming items + bool NavMoveForwardToNextFrame; + ImGuiNavMoveFlags NavMoveFlags; + ImGuiScrollFlags NavMoveScrollFlags; + ImGuiKeyModFlags NavMoveKeyMods; + ImGuiDir NavMoveDir; // Direction of the move request (left/right/up/down) + ImGuiDir NavMoveDirForDebug; ImGuiDir NavMoveClipDir; // FIXME-NAV: Describe the purpose of this better. Might want to rename? - ImGuiNavMoveResult NavMoveResultLocal; // Best move request candidate within NavWindow - ImGuiNavMoveResult NavMoveResultLocalVisibleSet; // Best move request candidate within NavWindow that are mostly visible (when using ImGuiNavMoveFlags_AlsoScoreVisibleSet flag) - ImGuiNavMoveResult NavMoveResultOther; // Best move request candidate within NavWindow's flattened hierarchy (when using ImGuiWindowFlags_NavFlattened flag) - ImGuiWindow* NavWrapRequestWindow; // Window which requested trying nav wrap-around. - ImGuiNavMoveFlags NavWrapRequestFlags; // Wrap-around operation flags. + ImRect NavScoringRect; // Rectangle used for scoring, in screen space. Based of window->NavRectRel[], modified for directional navigation scoring. + ImRect NavScoringNoClipRect; // Some nav operations (such as PageUp/PageDown) enforce a region which clipper will attempt to always keep submitted + int NavScoringDebugCount; // Metrics for debugging + int NavTabbingDir; // Generally -1 or +1, 0 when tabbing without a nav id + int NavTabbingCounter; // >0 when counting items for tabbing + ImGuiNavItemData NavMoveResultLocal; // Best move request candidate within NavWindow + ImGuiNavItemData NavMoveResultLocalVisible; // Best move request candidate within NavWindow that are mostly visible (when using ImGuiNavMoveFlags_AlsoScoreVisibleSet flag) + ImGuiNavItemData NavMoveResultOther; // Best move request candidate within NavWindow's flattened hierarchy (when using ImGuiWindowFlags_NavFlattened flag) + ImGuiNavItemData NavTabbingResultFirst; // First tabbing request candidate within NavWindow and flattened hierarchy // Navigation: Windowing (CTRL+TAB for list, or Menu button + keys or directional pads to move/resize) ImGuiWindow* NavWindowingTarget; // Target window when doing CTRL+Tab (or Pad Menu + FocusPrev/Next), this window is temporarily displayed top-most! @@ -1370,21 +1695,8 @@ struct ImGuiContext float NavWindowingHighlightAlpha; bool NavWindowingToggleLayer; - // Legacy Focus/Tabbing system (older than Nav, active even if Nav is disabled, misnamed. FIXME-NAV: This needs a redesign!) - ImGuiWindow* FocusRequestCurrWindow; // - ImGuiWindow* FocusRequestNextWindow; // - int FocusRequestCurrCounterRegular; // Any item being requested for focus, stored as an index (we on layout to be stable between the frame pressing TAB and the next frame, semi-ouch) - int FocusRequestCurrCounterTabStop; // Tab item being requested for focus, stored as an index - int FocusRequestNextCounterRegular; // Stored for next frame - int FocusRequestNextCounterTabStop; // " - bool FocusTabPressed; // - // Render - ImDrawData DrawData; // Main ImDrawData instance to pass render information to the user - ImDrawDataBuilder DrawDataBuilder; float DimBgRatio; // 0.0..1.0 animation when fading in a dimming background (for modal window and CTRL+TAB list) - ImDrawList BackgroundDrawList; // First draw list to be rendered. - ImDrawList ForegroundDrawList; // Last draw list to be rendered. This is where we the render software mouse cursor (if io.MouseDrawCursor is set) and most debug overlays. ImGuiMouseCursor MouseCursor; // Drag and Drop @@ -1406,10 +1718,15 @@ struct ImGuiContext ImVector DragDropPayloadBufHeap; // We don't expose the ImVector<> directly, ImGuiPayload only holds pointer+size unsigned char DragDropPayloadBufLocal[16]; // Local buffer for small payloads + // Clipper + int ClipperTempDataStacked; + ImVector ClipperTempData; + // Table ImGuiTable* CurrentTable; - ImPool Tables; - ImVector CurrentTableStack; + int TablesTempDataStacked; // Temporary table data size (because we leave previous instances undestructed, we generally don't use TablesTempData.Size) + ImVector TablesTempData; // Temporary table data (buffers reused/shared across instances, support nesting) + ImPool Tables; // Persistent table data ImVector TablesLastTimeActive; // Last used timestamp of each tables (SOA, for efficient GC) ImVector DrawChannelsTempMergeBuffer; @@ -1420,29 +1737,32 @@ struct ImGuiContext ImVector ShrinkWidthBuffer; // Widget state - ImVec2 LastValidMousePos; + ImVec2 MouseLastValidPos; ImGuiInputTextState InputTextState; ImFont InputTextPasswordFont; ImGuiID TempInputId; // Temporary text input when CTRL+clicking on a slider, etc. ImGuiColorEditFlags ColorEditOptions; // Store user options for color edit widgets - float ColorEditLastHue; // Backup of last Hue associated to LastColor[3], so we can restore Hue in lossy RGB<>HSV round trips - float ColorEditLastSat; // Backup of last Saturation associated to LastColor[3], so we can restore Saturation in lossy RGB<>HSV round trips - float ColorEditLastColor[3]; + float ColorEditLastHue; // Backup of last Hue associated to LastColor, so we can restore Hue in lossy RGB<>HSV round trips + float ColorEditLastSat; // Backup of last Saturation associated to LastColor, so we can restore Saturation in lossy RGB<>HSV round trips + ImU32 ColorEditLastColor; // RGB value with alpha set to 0. ImVec4 ColorPickerRef; // Initial/reference color at the time of opening the color picker. + ImGuiComboPreviewData ComboPreviewData; float SliderCurrentAccum; // Accumulated slider delta when using navigation controls. bool SliderCurrentAccumDirty; // Has the accumulated slider delta changed since last time we tried to apply it? bool DragCurrentAccumDirty; float DragCurrentAccum; // Accumulator for dragging modification. Always high-precision, not rounded by end-user precision settings float DragSpeedDefaultRatio; // If speed == 0.0f, uses (max-min) * DragSpeedDefaultRatio float ScrollbarClickDeltaToGrabCenter; // Distance between mouse and center of grab box, normalized in parent space. Use storage? - int TooltipOverrideCount; + float DisabledAlphaBackup; // Backup for style.Alpha for BeginDisabled() + short DisabledStackSize; + short TooltipOverrideCount; float TooltipSlowDelay; // Time before slow tooltips appears (FIXME: This is temporary until we merge in tooltip timer+priority work) ImVector ClipboardHandlerData; // If no custom clipboard handler is defined ImVector MenusIdSubmittedThisFrame; // A list of menu IDs that were rendered at least once // Platform support - ImVec2 PlatformImePos; // Cursor position request & last passed to the OS Input Method Editor - ImVec2 PlatformImeLastPos; + ImGuiPlatformImeData PlatformImeData; // Data updated by current frame + ImGuiPlatformImeData PlatformImeDataPrev; // Previous frame data (when changing we will call io.SetPlatformImeDataFn char PlatformLocaleDecimalPoint; // '.' or *localeconv()->decimal_point // Settings @@ -1453,12 +1773,15 @@ struct ImGuiContext ImChunkStream SettingsWindows; // ImGuiWindow .ini settings entries ImChunkStream SettingsTables; // ImGuiTable .ini settings entries ImVector Hooks; // Hooks for extensions (e.g. test engine) + ImGuiID HookIdNext; // Next available HookId // Capture/Logging bool LogEnabled; // Currently capturing ImGuiLogType LogType; // Capture target ImFileHandle LogFile; // If != NULL log to stdout/ file ImGuiTextBuffer LogBuffer; // Accumulation buffer when log to clipboard. This is pointer so our GImGui static constructor doesn't call heap allocators. + const char* LogNextPrefix; + const char* LogNextSuffix; float LogLinePosY; bool LogLineFirstItem; int LogDepthRef; @@ -1467,19 +1790,21 @@ struct ImGuiContext // Debug Tools bool DebugItemPickerActive; // Item picker is active (started with DebugStartItemPicker()) - ImGuiID DebugItemPickerBreakId; // Will call IM_DEBUG_BREAK() when encountering this id + ImGuiID DebugItemPickerBreakId; // Will call IM_DEBUG_BREAK() when encountering this ID ImGuiMetricsConfig DebugMetricsConfig; + ImGuiStackTool DebugStackTool; // Misc float FramerateSecPerFrame[120]; // Calculate estimate of framerate for user over the last 2 seconds. int FramerateSecPerFrameIdx; + int FramerateSecPerFrameCount; float FramerateSecPerFrameAccum; int WantCaptureMouseNextFrame; // Explicit capture via CaptureKeyboardFromApp()/CaptureMouseFromApp() sets those flags int WantCaptureKeyboardNextFrame; int WantTextInputNextFrame; char TempBuffer[1024 * 3 + 1]; // Temporary text buffer - ImGuiContext(ImFontAtlas* shared_font_atlas) : BackgroundDrawList(&DrawListSharedData), ForegroundDrawList(&DrawListSharedData) + ImGuiContext(ImFontAtlas* shared_font_atlas) { Initialized = false; FontAtlasOwnedByContext = shared_font_atlas ? false : true; @@ -1492,18 +1817,17 @@ struct ImGuiContext WithinFrameScope = WithinFrameScopeWithImplicitWindow = WithinEndChild = false; GcCompactAll = false; TestEngineHookItems = false; - TestEngineHookIdInfo = 0; TestEngine = NULL; WindowsActiveCount = 0; CurrentWindow = NULL; HoveredWindow = NULL; - HoveredRootWindow = NULL; HoveredWindowUnderMovingWindow = NULL; MovingWindow = NULL; WheelingWindow = NULL; WheelingWindowTimer = 0.0f; + DebugHookIdInfo = 0; HoveredId = HoveredIdPreviousFrame = 0; HoveredIdAllowOverlap = false; HoveredIdUsingMouseWheel = HoveredIdPreviousFrameUsingMouseWheel = false; @@ -1521,11 +1845,11 @@ struct ImGuiContext ActiveIdUsingMouseWheel = false; ActiveIdUsingNavDirMask = 0x00; ActiveIdUsingNavInputMask = 0x00; - ActiveIdUsingKeyInputMask = 0x00; + ActiveIdUsingKeyInputMask.ClearAllBits(); ActiveIdClickOffset = ImVec2(-1, -1); ActiveIdWindow = NULL; ActiveIdSource = ImGuiInputSource_None; - ActiveIdMouseButton = 0; + ActiveIdMouseButton = -1; ActiveIdPreviousFrame = 0; ActiveIdPreviousFrameIsAlive = false; ActiveIdPreviousFrameHasBeenEditedBefore = false; @@ -1533,15 +1857,16 @@ struct ImGuiContext LastActiveId = 0; LastActiveIdTimer = 0.0f; + CurrentItemFlags = ImGuiItemFlags_None; + BeginMenuCount = 0; + NavWindow = NULL; - NavId = NavFocusScopeId = NavActivateId = NavActivateDownId = NavActivatePressedId = NavInputId = 0; - NavJustTabbedId = NavJustMovedToId = NavJustMovedToFocusScopeId = NavNextActivateId = 0; + NavId = NavFocusScopeId = NavActivateId = NavActivateDownId = NavActivatePressedId = NavActivateInputId = 0; + NavJustMovedToId = NavJustMovedToFocusScopeId = NavNextActivateId = 0; + NavActivateFlags = NavNextActivateFlags = ImGuiActivateFlags_None; NavJustMovedToKeyMods = ImGuiKeyModFlags_None; NavInputSource = ImGuiInputSource_None; - NavScoringRect = ImRect(); - NavScoringCount = 0; NavLayer = ImGuiNavLayer_Main; - NavIdTabCounter = INT_MAX; NavIdIsAlive = false; NavMousePosDirty = false; NavDisableHighlight = true; @@ -1550,26 +1875,22 @@ struct ImGuiContext NavInitRequest = false; NavInitRequestFromMove = false; NavInitResultId = 0; - NavMoveRequest = false; - NavMoveRequestFlags = ImGuiNavMoveFlags_None; - NavMoveRequestForward = ImGuiNavForward_None; - NavMoveRequestKeyMods = ImGuiKeyModFlags_None; - NavMoveDir = NavMoveDirLast = NavMoveClipDir = ImGuiDir_None; - NavWrapRequestWindow = NULL; - NavWrapRequestFlags = ImGuiNavMoveFlags_None; + NavMoveSubmitted = false; + NavMoveScoringItems = false; + NavMoveForwardToNextFrame = false; + NavMoveFlags = ImGuiNavMoveFlags_None; + NavMoveScrollFlags = ImGuiScrollFlags_None; + NavMoveKeyMods = ImGuiKeyModFlags_None; + NavMoveDir = NavMoveDirForDebug = NavMoveClipDir = ImGuiDir_None; + NavScoringDebugCount = 0; + NavTabbingDir = 0; + NavTabbingCounter = 0; NavWindowingTarget = NavWindowingTargetAnim = NavWindowingListWindow = NULL; NavWindowingTimer = NavWindowingHighlightAlpha = 0.0f; NavWindowingToggleLayer = false; - FocusRequestCurrWindow = FocusRequestNextWindow = NULL; - FocusRequestCurrCounterRegular = FocusRequestCurrCounterTabStop = INT_MAX; - FocusRequestNextCounterRegular = FocusRequestNextCounterTabStop = INT_MAX; - FocusTabPressed = false; - DimBgRatio = 0.0f; - BackgroundDrawList._OwnerName = "##Background"; // Give it a name for debugging - ForegroundDrawList._OwnerName = "##Foreground"; // Give it a name for debugging MouseCursor = ImGuiMouseCursor_Arrow; DragDropActive = DragDropWithinSource = DragDropWithinTarget = false; @@ -1584,31 +1905,38 @@ struct ImGuiContext DragDropHoldJustPressedId = 0; memset(DragDropPayloadBufLocal, 0, sizeof(DragDropPayloadBufLocal)); + ClipperTempDataStacked = 0; + CurrentTable = NULL; + TablesTempDataStacked = 0; CurrentTabBar = NULL; - LastValidMousePos = ImVec2(0.0f, 0.0f); TempInputId = 0; - ColorEditOptions = ImGuiColorEditFlags__OptionsDefault; + ColorEditOptions = ImGuiColorEditFlags_DefaultOptions_; ColorEditLastHue = ColorEditLastSat = 0.0f; - ColorEditLastColor[0] = ColorEditLastColor[1] = ColorEditLastColor[2] = FLT_MAX; + ColorEditLastColor = 0; SliderCurrentAccum = 0.0f; SliderCurrentAccumDirty = false; DragCurrentAccumDirty = false; DragCurrentAccum = 0.0f; DragSpeedDefaultRatio = 1.0f / 100.0f; + DisabledAlphaBackup = 0.0f; + DisabledStackSize = 0; ScrollbarClickDeltaToGrabCenter = 0.0f; TooltipOverrideCount = 0; TooltipSlowDelay = 0.50f; - PlatformImePos = PlatformImeLastPos = ImVec2(FLT_MAX, FLT_MAX); + PlatformImeData.InputPos = ImVec2(0.0f, 0.0f); + PlatformImeDataPrev.InputPos = ImVec2(-1.0f, -1.0f); // Different to ensure initial submission PlatformLocaleDecimalPoint = '.'; SettingsLoaded = false; SettingsDirtyTimer = 0.0f; + HookIdNext = 0; LogEnabled = false; LogType = ImGuiLogType_None; + LogNextPrefix = LogNextSuffix = NULL; LogFile = NULL; LogLinePosY = FLT_MAX; LogLineFirstItem = false; @@ -1619,7 +1947,7 @@ struct ImGuiContext DebugItemPickerBreakId = 0; memset(FramerateSecPerFrame, 0, sizeof(FramerateSecPerFrame)); - FramerateSecPerFrameIdx = 0; + FramerateSecPerFrameIdx = FramerateSecPerFrameCount = 0; FramerateSecPerFrameAccum = 0.0f; WantCaptureMouseNextFrame = WantCaptureKeyboardNextFrame = WantTextInputNextFrame = -1; memset(TempBuffer, 0, sizeof(TempBuffer)); @@ -1648,17 +1976,12 @@ struct IMGUI_API ImGuiWindowTempData ImVec1 Indent; // Indentation / start position from left of window (increased by TreePush/TreePop, etc.) ImVec1 ColumnsOffset; // Offset to the current column (if ColumnsCurrent > 0). FIXME: This and the above should be a stack to allow use cases like Tree->Column->Tree. Need revamp columns API. ImVec1 GroupOffset; - - // Last item status - ImGuiID LastItemId; // ID for last item - ImGuiItemStatusFlags LastItemStatusFlags; // Status flags for last item (see ImGuiItemStatusFlags_) - ImRect LastItemRect; // Interaction rect for last item - ImRect LastItemDisplayRect; // End-user display rect for last item (only valid if LastItemStatusFlags & ImGuiItemStatusFlags_HasDisplayRect) + ImVec2 CursorStartPosLossyness;// Record the loss of precision of CursorStartPos due to really large scrolling amount. This is used by clipper to compensentate and fix the most common use case of large scroll area. // Keyboard/Gamepad navigation ImGuiNavLayer NavLayerCurrent; // Current layer, 0..31 (we currently only use 0..1) - int NavLayerActiveMask; // Which layers have been written to (result from previous frame) - int NavLayerActiveMaskNext; // Which layers have been written to (accumulator for current frame) + short NavLayersActiveMask; // Which layers have been written to (result from previous frame) + short NavLayersActiveMaskNext;// Which layers have been written to (accumulator for current frame) ImGuiID NavFocusScopeIdCurrent; // Current focus scope ID while appending bool NavHideHighlightOneFrame; bool NavHasScroll; // Set when scrolling can be used (ScrollMax > 0.0f) @@ -1675,17 +1998,13 @@ struct IMGUI_API ImGuiWindowTempData int CurrentTableIdx; // Current table index (into g.Tables) ImGuiLayoutType LayoutType; ImGuiLayoutType ParentLayoutType; // Layout type of parent window at the time of Begin() - int FocusCounterRegular; // (Legacy Focus/Tabbing system) Sequential counter, start at -1 and increase as assigned via FocusableItemRegister() (FIXME-NAV: Needs redesign) - int FocusCounterTabStop; // (Legacy Focus/Tabbing system) Same, but only count widgets which you can Tab through. // Local parameters stacks // We store the current settings outside of the vectors to increase memory locality (reduce cache misses). The vectors are rarely modified. Also it allows us to not heap allocate for short-lived windows which are not using those settings. - ImGuiItemFlags ItemFlags; // == g.ItemFlagsStack.back() - float ItemWidth; // == ItemWidthStack.back(). 0.0: default, >0.0: width in pixels, <0.0: align xx pixels to the right of window - float TextWrapPos; // == TextWrapPosStack.back() [empty == -1.0f] - ImVector ItemWidthStack; - ImVector TextWrapPosStack; - ImGuiStackSizes StackSizesOnBegin; // Store size of various stacks for asserting + float ItemWidth; // Current item width (>0.0: width in pixels, <0.0: align xx pixels to the right of window). + float TextWrapPos; // Current text wrap pos. + ImVector ItemWidthStack; // Store item widths to restore (attention: .back() is not == ItemWidth) + ImVector TextWrapPosStack; // Store text wrap pos to restore (attention: .back() is not == TextWrapPos) }; // Storage for one window @@ -1722,11 +2041,13 @@ struct IMGUI_API ImGuiWindow bool Appearing; // Set during the frame where the window is appearing (or re-appearing) bool Hidden; // Do not display (== HiddenFrames*** > 0) bool IsFallbackWindow; // Set on the "Debug##Default" window. + bool IsExplicitChild; // Set when passed _ChildWindow, left to false by BeginDocked() bool HasCloseButton; // Set when the window has a close button (p_open != NULL) signed char ResizeBorderHeld; // Current border being held for resize (-1: none, otherwise 0-3) short BeginCount; // Number of Begin() during the current frame (generally 0 or 1, 1+ if appending via multiple Begin/End pairs) - short BeginOrderWithinParent; // Order within immediate parent window, if we are a child window. Otherwise 0. - short BeginOrderWithinContext; // Order within entire imgui context. This is mostly used for debugging submission order related issues. + short BeginOrderWithinParent; // Begin() order within immediate parent window, if we are a child window. Otherwise 0. + short BeginOrderWithinContext; // Begin() order within entire imgui context. This is mostly used for debugging submission order related issues. + short FocusOrder; // Order within WindowsFocusOrder[], altered when windows are focused. ImGuiID PopupId; // ID in the popup stack when this window is used as a popup/menu (because we use generic Name/ID for recycling) ImS8 AutoFitFramesX, AutoFitFramesY; ImS8 AutoFitChildAxises; @@ -1735,6 +2056,7 @@ struct IMGUI_API ImGuiWindow ImS8 HiddenFramesCanSkipItems; // Hide the window for N frames ImS8 HiddenFramesCannotSkipItems; // Hide the window for N frames while allowing items to be submitted so we can measure their size ImS8 HiddenFramesForRenderOnly; // Hide the window until frame N at Render() time only + ImS8 DisableInputsFrames; // Disable window interactions for N frames ImGuiCond SetWindowPosAllowFlags : 8; // store acceptable condition flags for SetNextWindowPos() use. ImGuiCond SetWindowSizeAllowFlags : 8; // store acceptable condition flags for SetNextWindowSize() use. ImGuiCond SetWindowCollapsedAllowFlags : 8; // store acceptable condition flags for SetNextWindowCollapsed() use. @@ -1766,8 +2088,10 @@ struct IMGUI_API ImGuiWindow ImDrawList* DrawList; // == &DrawListInst (for backward compatibility reason with code using imgui_internal.h we keep this a pointer) ImDrawList DrawListInst; - ImGuiWindow* ParentWindow; // If we are a child _or_ popup window, this is pointing to our parent. Otherwise NULL. - ImGuiWindow* RootWindow; // Point to ourself or first ancestor that is not a child window == Top-level window. + ImGuiWindow* ParentWindow; // If we are a child _or_ popup _or_ docked window, this is pointing to our parent. Otherwise NULL. + ImGuiWindow* ParentWindowInBeginStack; + ImGuiWindow* RootWindow; // Point to ourself or first ancestor that is not a child window. Doesn't cross through popups/dock nodes. + ImGuiWindow* RootWindowPopupTree; // Point to ourself or first ancestor that is not a child window. Cross through popups parent<>child. ImGuiWindow* RootWindowForTitleBarHighlight; // Point to ourself or first ancestor which will display TitleBgActive color when this window is active. ImGuiWindow* RootWindowForNav; // Point to ourself or first ancestor which doesn't have the NavFlattened flag. @@ -1800,19 +2124,6 @@ struct IMGUI_API ImGuiWindow ImRect MenuBarRect() const { float y1 = Pos.y + TitleBarHeight(); return ImRect(Pos.x, y1, Pos.x + SizeFull.x, y1 + MenuBarHeight()); } }; -// Backup and restore just enough data to be able to use IsItemHovered() on item A after another B in the same window has overwritten the data. -struct ImGuiLastItemDataBackup -{ - ImGuiID LastItemId; - ImGuiItemStatusFlags LastItemStatusFlags; - ImRect LastItemRect; - ImRect LastItemDisplayRect; - - ImGuiLastItemDataBackup() { Backup(); } - void Backup() { ImGuiWindow* window = GImGui->CurrentWindow; LastItemId = window->DC.LastItemId; LastItemStatusFlags = window->DC.LastItemStatusFlags; LastItemRect = window->DC.LastItemRect; LastItemDisplayRect = window->DC.LastItemDisplayRect; } - void Restore() const { ImGuiWindow* window = GImGui->CurrentWindow; window->DC.LastItemId = LastItemId; window->DC.LastItemStatusFlags = LastItemStatusFlags; window->DC.LastItemRect = LastItemRect; window->DC.LastItemDisplayRect = LastItemDisplayRect; } -}; - //----------------------------------------------------------------------------- // [SECTION] Tab bar, Tab item support //----------------------------------------------------------------------------- @@ -1828,11 +2139,12 @@ enum ImGuiTabBarFlagsPrivate_ // Extend ImGuiTabItemFlags_ enum ImGuiTabItemFlagsPrivate_ { + ImGuiTabItemFlags_SectionMask_ = ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing, ImGuiTabItemFlags_NoCloseButton = 1 << 20, // Track whether p_open was set or not (we'll need this info on the next frame to recompute ContentWidth during layout) ImGuiTabItemFlags_Button = 1 << 21 // Used by TabItemButton, change the tab item behavior to mimic a button }; -// Storage for one active tab item (sizeof() 28~32 bytes) +// Storage for one active tab item (sizeof() 40 bytes) struct ImGuiTabItem { ImGuiID ID; @@ -1842,22 +2154,22 @@ struct ImGuiTabItem float Offset; // Position relative to beginning of tab float Width; // Width currently displayed float ContentWidth; // Width of label, stored during BeginTabItem() call - ImS16 NameOffset; // When Window==NULL, offset to name within parent ImGuiTabBar::TabsNames + ImS32 NameOffset; // When Window==NULL, offset to name within parent ImGuiTabBar::TabsNames ImS16 BeginOrder; // BeginTabItem() order, used to re-order tabs after toggling ImGuiTabBarFlags_Reorderable ImS16 IndexDuringLayout; // Index only used during TabBarLayout() bool WantClose; // Marked as closed by SetTabItemClosed() - ImGuiTabItem() { memset(this, 0, sizeof(*this)); LastFrameVisible = LastFrameSelected = -1; NameOffset = BeginOrder = IndexDuringLayout = -1; } + ImGuiTabItem() { memset(this, 0, sizeof(*this)); LastFrameVisible = LastFrameSelected = -1; NameOffset = -1; BeginOrder = IndexDuringLayout = -1; } }; // Storage for a tab bar (sizeof() 152 bytes) -struct ImGuiTabBar +struct IMGUI_API ImGuiTabBar { ImVector Tabs; ImGuiTabBarFlags Flags; ImGuiID ID; // Zero for tab-bars used by docking ImGuiID SelectedTabId; // Selected tab/window - ImGuiID NextSelectedTabId; + ImGuiID NextSelectedTabId; // Next selected tab/window. Will also trigger a scrolling animation ImGuiID VisibleTabId; // Can occasionally be != SelectedTabId (e.g. when previewing contents for CTRL+TAB preview) int CurrFrameVisible; int PrevFrameVisible; @@ -1873,7 +2185,7 @@ struct ImGuiTabBar float ScrollingRectMinX; float ScrollingRectMaxX; ImGuiID ReorderRequestTabId; - ImS8 ReorderRequestDir; + ImS16 ReorderRequestOffset; ImS8 BeginCount; bool WantLayout; bool VisibleTabWasSubmitted; @@ -1889,7 +2201,7 @@ struct ImGuiTabBar int GetTabOrder(const ImGuiTabItem* tab) const { return Tabs.index_from_ptr(tab); } const char* GetTabName(const ImGuiTabItem* tab) const { - IM_ASSERT(tab->NameOffset != -1 && (int)tab->NameOffset < TabsNames.Buf.Size); + IM_ASSERT(tab->NameOffset != -1 && tab->NameOffset < TabsNames.Buf.Size); return TabsNames.Buf.Data + tab->NameOffset; } }; @@ -1898,8 +2210,6 @@ struct ImGuiTabBar // [SECTION] Table support //----------------------------------------------------------------------------- -#ifdef IMGUI_HAS_TABLE - #define IM_COL32_DISABLE IM_COL32(0,0,0,1) // Special sentinel code which cannot be used as a regular color. #define IMGUI_TABLE_MAX_COLUMNS 64 // sizeof(ImU64) * 8. This is solely because we frequently encode columns set in a ImU64. #define IMGUI_TABLE_MAX_DRAW_CHANNELS (4 + 64 * 2) // See TableSetupDrawChannels() @@ -1938,10 +2248,11 @@ struct ImGuiTableColumn ImGuiTableColumnIdx NextEnabledColumn; // Index of next enabled/visible column within Columns[], -1 if last enabled/visible column ImGuiTableColumnIdx SortOrder; // Index of this column within sort specs, -1 if not sorting on this column, 0 for single-sort, may be >0 on multi-sort ImGuiTableDrawChannelIdx DrawChannelCurrent; // Index within DrawSplitter.Channels[] - ImGuiTableDrawChannelIdx DrawChannelFrozen; - ImGuiTableDrawChannelIdx DrawChannelUnfrozen; - bool IsEnabled; // Is the column not marked Hidden by the user? (even if off view, e.g. clipped by scrolling). - bool IsEnabledNextFrame; + ImGuiTableDrawChannelIdx DrawChannelFrozen; // Draw channels for frozen rows (often headers) + ImGuiTableDrawChannelIdx DrawChannelUnfrozen; // Draw channels for unfrozen rows + bool IsEnabled; // IsUserEnabled && (Flags & ImGuiTableColumnFlags_Disabled) == 0 + bool IsUserEnabled; // Is the column not marked Hidden by the user? (unrelated to being off view, e.g. clipped by scrolling). + bool IsUserEnabledNextFrame; bool IsVisibleX; // Is actually in view (e.g. overlapping the host window clipping rectangle, not scrolled). bool IsVisibleY; bool IsRequestOutput; // Return value for TableSetColumnIndex() / TableNextColumn(): whether we request user to output contents or not. @@ -1976,12 +2287,13 @@ struct ImGuiTableCellData ImGuiTableColumnIdx Column; // Column number }; -// FIXME-TABLE: transient data could be stored in a per-stacked table structure: DrawSplitter, SortSpecs, incoming RowData -struct ImGuiTable +// FIXME-TABLE: more transient data could be stored in a per-stacked table structure: DrawSplitter, SortSpecs, incoming RowData +struct IMGUI_API ImGuiTable { ImGuiID ID; ImGuiTableFlags Flags; void* RawData; // Single allocation to hold Columns[], DisplayOrderToIndex[] and RowCellData[] + ImGuiTableTempData* TempData; // Transient data while table is active. Point within g.CurrentTableStack[] ImSpan Columns; // Point within RawData[] ImSpan DisplayOrderToIndex; // Point within RawData[]. Store display order of columns (when not reordered, the values are 0...Count-1) ImSpan RowCellData; // Point within RawData[]. Store cells background requests for current row. @@ -2029,26 +2341,17 @@ struct ImGuiTable ImRect InnerRect; // InnerRect but without decoration. As with OuterRect, for non-scrolling tables, InnerRect.Max.y is ImRect WorkRect; ImRect InnerClipRect; - ImRect BgClipRect; // We use this to cpu-clip cell background color fill + ImRect BgClipRect; // We use this to cpu-clip cell background color fill, evolve during the frame as we cross frozen rows boundaries ImRect Bg0ClipRectForDrawCmd; // Actual ImDrawCmd clip rect for BG0/1 channel. This tends to be == OuterWindow->ClipRect at BeginTable() because output in BG0/BG1 is cpu-clipped ImRect Bg2ClipRectForDrawCmd; // Actual ImDrawCmd clip rect for BG2 channel. This tends to be a correct, tight-fit, because output to BG2 are done by widgets relying on regular ClipRect. ImRect HostClipRect; // This is used to check if we can eventually merge our columns draw calls into the current draw call of the current window. - ImRect HostBackupWorkRect; // Backup of InnerWindow->WorkRect at the end of BeginTable() - ImRect HostBackupParentWorkRect; // Backup of InnerWindow->ParentWorkRect at the end of BeginTable() ImRect HostBackupInnerClipRect; // Backup of InnerWindow->ClipRect during PushTableBackground()/PopTableBackground() - ImVec2 HostBackupPrevLineSize; // Backup of InnerWindow->DC.PrevLineSize at the end of BeginTable() - ImVec2 HostBackupCurrLineSize; // Backup of InnerWindow->DC.CurrLineSize at the end of BeginTable() - ImVec2 HostBackupCursorMaxPos; // Backup of InnerWindow->DC.CursorMaxPos at the end of BeginTable() - ImVec2 UserOuterSize; // outer_size.x passed to BeginTable() - ImVec1 HostBackupColumnsOffset; // Backup of OuterWindow->DC.ColumnsOffset at the end of BeginTable() - float HostBackupItemWidth; // Backup of OuterWindow->DC.ItemWidth at the end of BeginTable() - int HostBackupItemWidthStackSize;// Backup of OuterWindow->DC.ItemWidthStack.Size at the end of BeginTable() ImGuiWindow* OuterWindow; // Parent window for the table ImGuiWindow* InnerWindow; // Window holding the table data (== OuterWindow or a child window) ImGuiTextBuffer ColumnsNames; // Contiguous buffer holding columns names - ImDrawListSplitter DrawSplitter; // We carry our own ImDrawList splitter to allow recursion (FIXME: could be stored outside, worst case we need 1 splitter per recursing table) + ImDrawListSplitter* DrawSplitter; // Shortcut to TempData->DrawSplitter while in table. Isolate draw commands per columns to avoid switching clip rect constantly ImGuiTableColumnSortSpecs SortSpecsSingle; - ImVector SortSpecsMulti; // FIXME-OPT: Using a small-vector pattern would work be good. + ImVector SortSpecsMulti; // FIXME-OPT: Using a small-vector pattern would be good. ImGuiTableSortSpecs SortSpecs; // Public facing sorts specs, this is what we return in TableGetSortSpecs() ImGuiTableColumnIdx SortSpecsCount; ImGuiTableColumnIdx ColumnsEnabledCount; // Number of enabled columns (<= ColumnsCount) @@ -2062,9 +2365,10 @@ struct ImGuiTable ImGuiTableColumnIdx HeldHeaderColumn; // Index of column header being held. ImGuiTableColumnIdx ReorderColumn; // Index of column being reordered. (not cleared) ImGuiTableColumnIdx ReorderColumnDir; // -1 or +1 + ImGuiTableColumnIdx LeftMostEnabledColumn; // Index of left-most non-hidden column. + ImGuiTableColumnIdx RightMostEnabledColumn; // Index of right-most non-hidden column. ImGuiTableColumnIdx LeftMostStretchedColumn; // Index of left-most stretched column. ImGuiTableColumnIdx RightMostStretchedColumn; // Index of right-most stretched column. - ImGuiTableColumnIdx RightMostEnabledColumn; // Index of right-most non-hidden column. ImGuiTableColumnIdx ContextPopupColumn; // Column right-clicked on, of -1 if opening context menu from a neutral/empty spot ImGuiTableColumnIdx FreezeRowsRequest; // Requested frozen rows count ImGuiTableColumnIdx FreezeRowsCount; // Actual frozen row count (== FreezeRowsRequest, or == 0 when no scrolling offset) @@ -2072,7 +2376,7 @@ struct ImGuiTable ImGuiTableColumnIdx FreezeColumnsCount; // Actual frozen columns count (== FreezeColumnsRequest, or == 0 when no scrolling offset) ImGuiTableColumnIdx RowCellDataCurrent; // Index of current RowCellData[] entry in current row ImGuiTableDrawChannelIdx DummyDrawChannel; // Redirect non-visible columns here. - ImGuiTableDrawChannelIdx Bg2DrawChannelCurrent; // For Selectable() and other widgets drawing accross columns after the freezing line. Index within DrawSplitter.Channels[] + ImGuiTableDrawChannelIdx Bg2DrawChannelCurrent; // For Selectable() and other widgets drawing across columns after the freezing line. Index within DrawSplitter.Channels[] ImGuiTableDrawChannelIdx Bg2DrawChannelUnfrozen; bool IsLayoutLocked; // Set by TableUpdateLayout() which is called when beginning the first row. bool IsInsideRow; // Set when inside TableBeginRow()/TableEndRow(). @@ -2086,12 +2390,35 @@ struct ImGuiTable bool IsResetAllRequest; bool IsResetDisplayOrderRequest; bool IsUnfrozenRows; // Set when we got past the frozen row. - bool IsDefaultSizingPolicy; // Set if user didn't explicitely set a sizing policy in BeginTable() + bool IsDefaultSizingPolicy; // Set if user didn't explicitly set a sizing policy in BeginTable() bool MemoryCompacted; bool HostSkipItems; // Backup of InnerWindow->SkipItem at the end of BeginTable(), because we will overwrite InnerWindow->SkipItem on a per-column basis - IMGUI_API ImGuiTable() { memset(this, 0, sizeof(*this)); LastFrameActive = -1; } - IMGUI_API ~ImGuiTable() { IM_FREE(RawData); } + ImGuiTable() { memset(this, 0, sizeof(*this)); LastFrameActive = -1; } + ~ImGuiTable() { IM_FREE(RawData); } +}; + +// Transient data that are only needed between BeginTable() and EndTable(), those buffers are shared (1 per level of stacked table). +// - Accessing those requires chasing an extra pointer so for very frequently used data we leave them in the main table structure. +// - We also leave out of this structure data that tend to be particularly useful for debugging/metrics. +struct IMGUI_API ImGuiTableTempData +{ + int TableIndex; // Index in g.Tables.Buf[] pool + float LastTimeActive; // Last timestamp this structure was used + + ImVec2 UserOuterSize; // outer_size.x passed to BeginTable() + ImDrawListSplitter DrawSplitter; + + ImRect HostBackupWorkRect; // Backup of InnerWindow->WorkRect at the end of BeginTable() + ImRect HostBackupParentWorkRect; // Backup of InnerWindow->ParentWorkRect at the end of BeginTable() + ImVec2 HostBackupPrevLineSize; // Backup of InnerWindow->DC.PrevLineSize at the end of BeginTable() + ImVec2 HostBackupCurrLineSize; // Backup of InnerWindow->DC.CurrLineSize at the end of BeginTable() + ImVec2 HostBackupCursorMaxPos; // Backup of InnerWindow->DC.CursorMaxPos at the end of BeginTable() + ImVec1 HostBackupColumnsOffset; // Backup of OuterWindow->DC.ColumnsOffset at the end of BeginTable() + float HostBackupItemWidth; // Backup of OuterWindow->DC.ItemWidth at the end of BeginTable() + int HostBackupItemWidthStackSize;//Backup of OuterWindow->DC.ItemWidthStack.Size at the end of BeginTable() + + ImGuiTableTempData() { memset(this, 0, sizeof(*this)); LastTimeActive = -1.0f; } }; // sizeof() ~ 12 @@ -2132,10 +2459,8 @@ struct ImGuiTableSettings ImGuiTableColumnSettings* GetColumnSettings() { return (ImGuiTableColumnSettings*)(this + 1); } }; -#endif // #ifdef IMGUI_HAS_TABLE - //----------------------------------------------------------------------------- -// [SECTION] Internal API +// [SECTION] ImGui internal API // No guarantee of forward compatibility here! //----------------------------------------------------------------------------- @@ -2152,14 +2477,16 @@ namespace ImGui IMGUI_API ImGuiWindow* FindWindowByName(const char* name); IMGUI_API void UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window); IMGUI_API ImVec2 CalcWindowNextAutoFitSize(ImGuiWindow* window); - IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent); + IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool popup_hierarchy); + IMGUI_API bool IsWindowWithinBeginStackOf(ImGuiWindow* window, ImGuiWindow* potential_parent); IMGUI_API bool IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below); IMGUI_API bool IsWindowNavFocusable(ImGuiWindow* window); - IMGUI_API ImRect GetWindowAllowedExtentRect(ImGuiWindow* window); IMGUI_API void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond = 0); IMGUI_API void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond = 0); IMGUI_API void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond = 0); IMGUI_API void SetWindowHitTestHole(ImGuiWindow* window, const ImVec2& pos, const ImVec2& size); + inline ImRect WindowRectAbsToRel(ImGuiWindow* window, const ImRect& r) { ImVec2 off = window->DC.CursorStartPos; return ImRect(r.Min.x - off.x, r.Min.y - off.y, r.Max.x - off.x, r.Max.y - off.y); } + inline ImRect WindowRectRelToAbs(ImGuiWindow* window, const ImRect& r) { ImVec2 off = window->DC.CursorStartPos; return ImRect(r.Min.x + off.x, r.Min.y + off.y, r.Max.x + off.x, r.Max.y + off.y); } // Windows: Display Order and Focus Order IMGUI_API void FocusWindow(ImGuiWindow* window); @@ -2167,24 +2494,31 @@ namespace ImGui IMGUI_API void BringWindowToFocusFront(ImGuiWindow* window); IMGUI_API void BringWindowToDisplayFront(ImGuiWindow* window); IMGUI_API void BringWindowToDisplayBack(ImGuiWindow* window); + IMGUI_API void BringWindowToDisplayBehind(ImGuiWindow* window, ImGuiWindow* above_window); + IMGUI_API int FindWindowDisplayIndex(ImGuiWindow* window); + IMGUI_API ImGuiWindow* FindBottomMostVisibleWindowWithinBeginStack(ImGuiWindow* window); // Fonts, drawing IMGUI_API void SetCurrentFont(ImFont* font); inline ImFont* GetDefaultFont() { ImGuiContext& g = *GImGui; return g.IO.FontDefault ? g.IO.FontDefault : g.IO.Fonts->Fonts[0]; } - inline ImDrawList* GetForegroundDrawList(ImGuiWindow* window) { IM_UNUSED(window); ImGuiContext& g = *GImGui; return &g.ForegroundDrawList; } // This seemingly unnecessary wrapper simplifies compatibility between the 'master' and 'docking' branches. + inline ImDrawList* GetForegroundDrawList(ImGuiWindow* window) { IM_UNUSED(window); return GetForegroundDrawList(); } // This seemingly unnecessary wrapper simplifies compatibility between the 'master' and 'docking' branches. + IMGUI_API ImDrawList* GetBackgroundDrawList(ImGuiViewport* viewport); // get background draw list for the given viewport. this draw list will be the first rendering one. Useful to quickly draw shapes/text behind dear imgui contents. + IMGUI_API ImDrawList* GetForegroundDrawList(ImGuiViewport* viewport); // get foreground draw list for the given viewport. this draw list will be the last rendered one. Useful to quickly draw shapes/text over dear imgui contents. // Init IMGUI_API void Initialize(ImGuiContext* context); IMGUI_API void Shutdown(ImGuiContext* context); // Since 1.60 this is a _private_ function. You can call DestroyContext() to destroy the context created by CreateContext(). // NewFrame + IMGUI_API void UpdateInputEvents(bool trickle_fast_inputs); IMGUI_API void UpdateHoveredWindowAndCaptureFlags(); IMGUI_API void StartMouseMovingWindow(ImGuiWindow* window); IMGUI_API void UpdateMouseMovingWindowNewFrame(); IMGUI_API void UpdateMouseMovingWindowEndFrame(); // Generic context hooks - IMGUI_API void AddContextHook(ImGuiContext* context, const ImGuiContextHook* hook); + IMGUI_API ImGuiID AddContextHook(ImGuiContext* context, const ImGuiContextHook* hook); + IMGUI_API void RemoveContextHook(ImGuiContext* context, ImGuiID hook_to_remove); IMGUI_API void CallContextHooks(ImGuiContext* context, ImGuiContextHookType type); // Settings @@ -2202,14 +2536,21 @@ namespace ImGui IMGUI_API void SetScrollY(ImGuiWindow* window, float scroll_y); IMGUI_API void SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio); IMGUI_API void SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio); - IMGUI_API ImVec2 ScrollToBringRectIntoView(ImGuiWindow* window, const ImRect& item_rect); + + // Early work-in-progress API (ScrollToItem() will become public) + IMGUI_API void ScrollToItem(ImGuiScrollFlags flags = 0); + IMGUI_API void ScrollToRect(ImGuiWindow* window, const ImRect& rect, ImGuiScrollFlags flags = 0); + IMGUI_API ImVec2 ScrollToRectEx(ImGuiWindow* window, const ImRect& rect, ImGuiScrollFlags flags = 0); +//#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + inline void ScrollToBringRectIntoView(ImGuiWindow* window, const ImRect& rect) { ScrollToRect(window, rect, ImGuiScrollFlags_KeepVisibleEdgeY); } +//#endif // Basic Accessors - inline ImGuiID GetItemID() { ImGuiContext& g = *GImGui; return g.CurrentWindow->DC.LastItemId; } // Get ID of last item (~~ often same ImGui::GetID(label) beforehand) - inline ImGuiItemStatusFlags GetItemStatusFlags() { ImGuiContext& g = *GImGui; return g.CurrentWindow->DC.LastItemStatusFlags; } + inline ImGuiID GetItemID() { ImGuiContext& g = *GImGui; return g.LastItemData.ID; } // Get ID of last item (~~ often same ImGui::GetID(label) beforehand) + inline ImGuiItemStatusFlags GetItemStatusFlags(){ ImGuiContext& g = *GImGui; return g.LastItemData.StatusFlags; } + inline ImGuiItemFlags GetItemFlags() { ImGuiContext& g = *GImGui; return g.LastItemData.InFlags; } inline ImGuiID GetActiveID() { ImGuiContext& g = *GImGui; return g.ActiveId; } inline ImGuiID GetFocusID() { ImGuiContext& g = *GImGui; return g.NavId; } - inline ImGuiItemFlags GetItemsFlags() { ImGuiContext& g = *GImGui; return g.CurrentWindow->DC.ItemFlags; } IMGUI_API void SetActiveID(ImGuiID id, ImGuiWindow* window); IMGUI_API void SetFocusID(ImGuiID id, ImGuiWindow* window); IMGUI_API void ClearActiveID(); @@ -2223,49 +2564,79 @@ namespace ImGui // Basic Helpers for widget code IMGUI_API void ItemSize(const ImVec2& size, float text_baseline_y = -1.0f); IMGUI_API void ItemSize(const ImRect& bb, float text_baseline_y = -1.0f); - IMGUI_API bool ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb = NULL); + IMGUI_API bool ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb = NULL, ImGuiItemFlags extra_flags = 0); IMGUI_API bool ItemHoverable(const ImRect& bb, ImGuiID id); - IMGUI_API bool IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged); - IMGUI_API void SetLastItemData(ImGuiWindow* window, ImGuiID item_id, ImGuiItemStatusFlags status_flags, const ImRect& item_rect); - IMGUI_API bool FocusableItemRegister(ImGuiWindow* window, ImGuiID id); // Return true if focus is requested - IMGUI_API void FocusableItemUnregister(ImGuiWindow* window); + IMGUI_API bool IsClippedEx(const ImRect& bb, ImGuiID id); + IMGUI_API void SetLastItemData(ImGuiID item_id, ImGuiItemFlags in_flags, ImGuiItemStatusFlags status_flags, const ImRect& item_rect); IMGUI_API ImVec2 CalcItemSize(ImVec2 size, float default_w, float default_h); IMGUI_API float CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x); IMGUI_API void PushMultiItemsWidths(int components, float width_full); - IMGUI_API void PushItemFlag(ImGuiItemFlags option, bool enabled); - IMGUI_API void PopItemFlag(); IMGUI_API bool IsItemToggledSelection(); // Was the last item selection toggled? (after Selectable(), TreeNode() etc. We only returns toggle _event_ in order to handle clipping correctly) IMGUI_API ImVec2 GetContentRegionMaxAbs(); IMGUI_API void ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_excess); + // Parameter stacks + IMGUI_API void PushItemFlag(ImGuiItemFlags option, bool enabled); + IMGUI_API void PopItemFlag(); + +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + // Currently refactoring focus/nav/tabbing system + // If you have old/custom copy-and-pasted widgets that used FocusableItemRegister(): + // (Old) IMGUI_VERSION_NUM < 18209: using 'ItemAdd(....)' and 'bool tab_focused = FocusableItemRegister(...)' + // (Old) IMGUI_VERSION_NUM >= 18209: using 'ItemAdd(..., ImGuiItemAddFlags_Focusable)' and 'bool tab_focused = (GetItemStatusFlags() & ImGuiItemStatusFlags_Focused) != 0' + // (New) IMGUI_VERSION_NUM >= 18413: using 'ItemAdd(..., ImGuiItemFlags_Inputable)' and 'bool tab_focused = (GetItemStatusFlags() & ImGuiItemStatusFlags_FocusedTabbing) != 0 || g.NavActivateInputId == id' (WIP) + // Widget code are simplified as there's no need to call FocusableItemUnregister() while managing the transition from regular widget to TempInputText() + inline bool FocusableItemRegister(ImGuiWindow* window, ImGuiID id) { IM_ASSERT(0); IM_UNUSED(window); IM_UNUSED(id); return false; } // -> pass ImGuiItemAddFlags_Inputable flag to ItemAdd() + inline void FocusableItemUnregister(ImGuiWindow* window) { IM_ASSERT(0); IM_UNUSED(window); } // -> unnecessary: TempInputText() uses ImGuiInputTextFlags_MergedItem +#endif + // Logging/Capture IMGUI_API void LogBegin(ImGuiLogType type, int auto_open_depth); // -> BeginCapture() when we design v2 api, for now stay under the radar by using the old name. IMGUI_API void LogToBuffer(int auto_open_depth = -1); // Start logging/capturing to internal buffer + IMGUI_API void LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end = NULL); + IMGUI_API void LogSetNextTextDecoration(const char* prefix, const char* suffix); // Popups, Modals, Tooltips IMGUI_API bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags); IMGUI_API void OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags = ImGuiPopupFlags_None); IMGUI_API void ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup); IMGUI_API void ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup); + IMGUI_API void ClosePopupsExceptModals(); IMGUI_API bool IsPopupOpen(ImGuiID id, ImGuiPopupFlags popup_flags); IMGUI_API bool BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags); - IMGUI_API void BeginTooltipEx(ImGuiWindowFlags extra_flags, ImGuiTooltipFlags tooltip_flags); + IMGUI_API void BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags extra_window_flags); + IMGUI_API ImRect GetPopupAllowedExtentRect(ImGuiWindow* window); IMGUI_API ImGuiWindow* GetTopMostPopupModal(); + IMGUI_API ImGuiWindow* GetTopMostAndVisiblePopupModal(); IMGUI_API ImVec2 FindBestWindowPosForPopup(ImGuiWindow* window); IMGUI_API ImVec2 FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy); + // Menus + IMGUI_API bool BeginViewportSideBar(const char* name, ImGuiViewport* viewport, ImGuiDir dir, float size, ImGuiWindowFlags window_flags); + IMGUI_API bool BeginMenuEx(const char* label, const char* icon, bool enabled = true); + IMGUI_API bool MenuItemEx(const char* label, const char* icon, const char* shortcut = NULL, bool selected = false, bool enabled = true); + + // Combos + IMGUI_API bool BeginComboPopup(ImGuiID popup_id, const ImRect& bb, ImGuiComboFlags flags); + IMGUI_API bool BeginComboPreview(); + IMGUI_API void EndComboPreview(); + // Gamepad/Keyboard Navigation IMGUI_API void NavInitWindow(ImGuiWindow* window, bool force_reinit); + IMGUI_API void NavInitRequestApplyResult(); IMGUI_API bool NavMoveRequestButNoResultYet(); + IMGUI_API void NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags); + IMGUI_API void NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags); + IMGUI_API void NavMoveRequestResolveWithLastItem(ImGuiNavItemData* result); IMGUI_API void NavMoveRequestCancel(); - IMGUI_API void NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, const ImRect& bb_rel, ImGuiNavMoveFlags move_flags); + IMGUI_API void NavMoveRequestApplyResult(); IMGUI_API void NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags); + IMGUI_API const char* GetNavInputName(ImGuiNavInput n); IMGUI_API float GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode); IMGUI_API ImVec2 GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor = 0.0f, float fast_factor = 0.0f); IMGUI_API int CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate); IMGUI_API void ActivateItem(ImGuiID id); // Remotely activate a button, checkbox, tree node etc. given its unique ID. activation is queued and processed on the next frame when the item is encountered again. - IMGUI_API void SetNavID(ImGuiID id, int nav_layer, ImGuiID focus_scope_id); - IMGUI_API void SetNavIDWithRectRel(ImGuiID id, int nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel); + IMGUI_API void SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel); // Focus Scope (WIP) // This is generally used to identify a selection set (multiple of which may be in the same window), as selection @@ -2277,15 +2648,23 @@ namespace ImGui // Inputs // FIXME: Eventually we should aim to move e.g. IsActiveIdUsingKey() into IsKeyXXX functions. + inline bool IsNamedKey(ImGuiKey key) { return key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END; } + inline bool IsLegacyKey(ImGuiKey key) { return key >= ImGuiKey_LegacyNativeKey_BEGIN && key < ImGuiKey_LegacyNativeKey_END; } + inline bool IsGamepadKey(ImGuiKey key) { return key >= ImGuiKey_Gamepad_BEGIN && key < ImGuiKey_Gamepad_END; } + IMGUI_API ImGuiKeyData* GetKeyData(ImGuiKey key); IMGUI_API void SetItemUsingMouseWheel(); + IMGUI_API void SetActiveIdUsingNavAndKeys(); inline bool IsActiveIdUsingNavDir(ImGuiDir dir) { ImGuiContext& g = *GImGui; return (g.ActiveIdUsingNavDirMask & (1 << dir)) != 0; } inline bool IsActiveIdUsingNavInput(ImGuiNavInput input) { ImGuiContext& g = *GImGui; return (g.ActiveIdUsingNavInputMask & (1 << input)) != 0; } - inline bool IsActiveIdUsingKey(ImGuiKey key) { ImGuiContext& g = *GImGui; IM_ASSERT(key < 64); return (g.ActiveIdUsingKeyInputMask & ((ImU64)1 << key)) != 0; } + inline bool IsActiveIdUsingKey(ImGuiKey key) { ImGuiContext& g = *GImGui; return g.ActiveIdUsingKeyInputMask[key]; } + inline void SetActiveIdUsingKey(ImGuiKey key) { ImGuiContext& g = *GImGui; g.ActiveIdUsingKeyInputMask.SetBit(key); } IMGUI_API bool IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold = -1.0f); - inline bool IsKeyPressedMap(ImGuiKey key, bool repeat = true) { ImGuiContext& g = *GImGui; const int key_index = g.IO.KeyMap[key]; return (key_index >= 0) ? IsKeyPressed(key_index, repeat) : false; } inline bool IsNavInputDown(ImGuiNavInput n) { ImGuiContext& g = *GImGui; return g.IO.NavInputs[n] > 0.0f; } inline bool IsNavInputTest(ImGuiNavInput n, ImGuiInputReadMode rm) { return (GetNavInputAmount(n, rm) > 0.0f); } IMGUI_API ImGuiKeyModFlags GetMergedKeyModFlags(); +#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO + inline bool IsKeyPressedMap(ImGuiKey key, bool repeat = true) { IM_ASSERT(IsNamedKey(key)); return IsKeyPressed(key, repeat); } +#endif // Drag and Drop IMGUI_API bool BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id); @@ -2314,6 +2693,7 @@ namespace ImGui IMGUI_API void TablePopBackgroundChannel(); // Tables: Internals + inline ImGuiTable* GetCurrentTable() { ImGuiContext& g = *GImGui; return g.CurrentTable; } IMGUI_API ImGuiTable* TableFindByID(ImGuiID id); IMGUI_API bool BeginTableEx(const char* name, ImGuiID id, int columns_count, ImGuiTableFlags flags = 0, const ImVec2& outer_size = ImVec2(0, 0), float inner_width = 0.0f); IMGUI_API void TableBeginInitMemory(ImGuiTable* table, int columns_count); @@ -2342,6 +2722,7 @@ namespace ImGui IMGUI_API void TableSetColumnWidthAutoAll(ImGuiTable* table); IMGUI_API void TableRemove(ImGuiTable* table); IMGUI_API void TableGcCompactTransientBuffers(ImGuiTable* table); + IMGUI_API void TableGcCompactTransientBuffers(ImGuiTableTempData* table); IMGUI_API void TableGcCompactSettings(); // Tables: Settings @@ -2358,7 +2739,8 @@ namespace ImGui IMGUI_API ImGuiTabItem* TabBarFindTabByID(ImGuiTabBar* tab_bar, ImGuiID tab_id); IMGUI_API void TabBarRemoveTab(ImGuiTabBar* tab_bar, ImGuiID tab_id); IMGUI_API void TabBarCloseTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab); - IMGUI_API void TabBarQueueReorder(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, int dir); + IMGUI_API void TabBarQueueReorder(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, int offset); + IMGUI_API void TabBarQueueReorderFromMousePos(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, ImVec2 mouse_pos); IMGUI_API bool TabBarProcessReorder(ImGuiTabBar* tab_bar); IMGUI_API bool TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags); IMGUI_API ImVec2 TabItemCalcSize(const char* label, bool has_close_button); @@ -2375,10 +2757,9 @@ namespace ImGui IMGUI_API void RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float clip_max_x, float ellipsis_max_x, const char* text, const char* text_end, const ImVec2* text_size_if_known); IMGUI_API void RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border = true, float rounding = 0.0f); IMGUI_API void RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding = 0.0f); - IMGUI_API void RenderColorRectWithAlphaCheckerboard(ImDrawList* draw_list, ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, float grid_step, ImVec2 grid_off, float rounding = 0.0f, int rounding_corners_flags = ~0); + IMGUI_API void RenderColorRectWithAlphaCheckerboard(ImDrawList* draw_list, ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, float grid_step, ImVec2 grid_off, float rounding = 0.0f, ImDrawFlags flags = 0); IMGUI_API void RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags = ImGuiNavHighlightFlags_TypeDefault); // Navigation highlight IMGUI_API const char* FindRenderedTextEnd(const char* text, const char* text_end = NULL); // Find the optional ## from which we stop displaying text. - IMGUI_API void LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end = NULL); // Render helpers (those functions don't access any ImGui state!) IMGUI_API void RenderArrow(ImDrawList* draw_list, ImVec2 pos, ImU32 col, ImGuiDir dir, float scale = 1.0f); @@ -2402,11 +2783,12 @@ namespace ImGui IMGUI_API bool CollapseButton(ImGuiID id, const ImVec2& pos); IMGUI_API bool ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size_arg, ImGuiButtonFlags flags = 0); IMGUI_API void Scrollbar(ImGuiAxis axis); - IMGUI_API bool ScrollbarEx(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* p_scroll_v, float avail_v, float contents_v, ImDrawCornerFlags rounding_corners); + IMGUI_API bool ScrollbarEx(const ImRect& bb, ImGuiID id, ImGuiAxis axis, ImS64* p_scroll_v, ImS64 avail_v, ImS64 contents_v, ImDrawFlags flags); IMGUI_API bool ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec2& padding, const ImVec4& bg_col, const ImVec4& tint_col); IMGUI_API ImRect GetWindowScrollbarRect(ImGuiWindow* window, ImGuiAxis axis); IMGUI_API ImGuiID GetWindowScrollbarID(ImGuiWindow* window, ImGuiAxis axis); - IMGUI_API ImGuiID GetWindowResizeID(ImGuiWindow* window, int n); // 0..3: corners, 4..7: borders + IMGUI_API ImGuiID GetWindowResizeCornerID(ImGuiWindow* window, int n); // 0..3: corners + IMGUI_API ImGuiID GetWindowResizeBorderID(ImGuiWindow* window, ImGuiDir dir); IMGUI_API void SeparatorEx(ImGuiSeparatorFlags flags); IMGUI_API bool CheckboxFlags(const char* label, ImS64* flags, ImS64 flags_value); IMGUI_API bool CheckboxFlags(const char* label, ImU64* flags, ImU64 flags_value); @@ -2434,7 +2816,7 @@ namespace ImGui IMGUI_API const ImGuiDataTypeInfo* DataTypeGetInfo(ImGuiDataType data_type); IMGUI_API int DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* p_data, const char* format); IMGUI_API void DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, const void* arg_1, const void* arg_2); - IMGUI_API bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* p_data, const char* format); + IMGUI_API bool DataTypeApplyFromText(const char* buf, ImGuiDataType data_type, void* p_data, const char* format); IMGUI_API int DataTypeCompare(ImGuiDataType data_type, const void* arg_1, const void* arg_2); IMGUI_API bool DataTypeClamp(ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max); @@ -2464,12 +2846,16 @@ namespace ImGui // Debug Tools IMGUI_API void ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, void* user_data = NULL); - inline void DebugDrawItemRect(ImU32 col = IM_COL32(255,0,0,255)) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; GetForegroundDrawList(window)->AddRect(window->DC.LastItemRect.Min, window->DC.LastItemRect.Max, col); } + IMGUI_API void ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, void* user_data = NULL); + inline void DebugDrawItemRect(ImU32 col = IM_COL32(255,0,0,255)) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; GetForegroundDrawList(window)->AddRect(g.LastItemData.Rect.Min, g.LastItemData.Rect.Max, col); } inline void DebugStartItemPicker() { ImGuiContext& g = *GImGui; g.DebugItemPickerActive = true; } + IMGUI_API void ShowFontAtlas(ImFontAtlas* atlas); + IMGUI_API void DebugHookIdInfo(ImGuiID id, ImGuiDataType data_type, const void* data_id, const void* data_id_end); IMGUI_API void DebugNodeColumns(ImGuiOldColumns* columns); IMGUI_API void DebugNodeDrawList(ImGuiWindow* window, const ImDrawList* draw_list, const char* label); - IMGUI_API void DebugNodeDrawCmdShowMeshAndBoundingBox(ImGuiWindow* window, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, bool show_mesh, bool show_aabb); + IMGUI_API void DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, bool show_mesh, bool show_aabb); + IMGUI_API void DebugNodeFont(ImFont* font); IMGUI_API void DebugNodeStorage(ImGuiStorage* storage, const char* label); IMGUI_API void DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label); IMGUI_API void DebugNodeTable(ImGuiTable* table); @@ -2477,40 +2863,52 @@ namespace ImGui IMGUI_API void DebugNodeWindow(ImGuiWindow* window, const char* label); IMGUI_API void DebugNodeWindowSettings(ImGuiWindowSettings* settings); IMGUI_API void DebugNodeWindowsList(ImVector* windows, const char* label); + IMGUI_API void DebugNodeWindowsListByBeginStackParent(ImGuiWindow** windows, int windows_size, ImGuiWindow* parent_in_begin_stack); + IMGUI_API void DebugNodeViewport(ImGuiViewportP* viewport); + IMGUI_API void DebugRenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP* viewport, const ImRect& bb); } // namespace ImGui -// ImFontAtlas internals -IMGUI_API bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas); -IMGUI_API void ImFontAtlasBuildInit(ImFontAtlas* atlas); -IMGUI_API void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* font_config, float ascent, float descent); -IMGUI_API void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* stbrp_context_opaque); -IMGUI_API void ImFontAtlasBuildFinish(ImFontAtlas* atlas); -IMGUI_API void ImFontAtlasBuildRender1bppRectFromString(ImFontAtlas* atlas, int atlas_x, int atlas_y, int w, int h, const char* in_str, char in_marker_char, unsigned char in_marker_pixel_value); -IMGUI_API void ImFontAtlasBuildMultiplyCalcLookupTable(unsigned char out_table[256], float in_multiply_factor); -IMGUI_API void ImFontAtlasBuildMultiplyRectAlpha8(const unsigned char table[256], unsigned char* pixels, int x, int y, int w, int h, int stride); + +//----------------------------------------------------------------------------- +// [SECTION] ImFontAtlas internal API +//----------------------------------------------------------------------------- + +// This structure is likely to evolve as we add support for incremental atlas updates +struct ImFontBuilderIO +{ + bool (*FontBuilder_Build)(ImFontAtlas* atlas); +}; + +// Helper for font builder +#ifdef IMGUI_ENABLE_STB_TRUETYPE +IMGUI_API const ImFontBuilderIO* ImFontAtlasGetBuilderForStbTruetype(); +#endif +IMGUI_API void ImFontAtlasBuildInit(ImFontAtlas* atlas); +IMGUI_API void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* font_config, float ascent, float descent); +IMGUI_API void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* stbrp_context_opaque); +IMGUI_API void ImFontAtlasBuildFinish(ImFontAtlas* atlas); +IMGUI_API void ImFontAtlasBuildRender8bppRectFromString(ImFontAtlas* atlas, int x, int y, int w, int h, const char* in_str, char in_marker_char, unsigned char in_marker_pixel_value); +IMGUI_API void ImFontAtlasBuildRender32bppRectFromString(ImFontAtlas* atlas, int x, int y, int w, int h, const char* in_str, char in_marker_char, unsigned int in_marker_pixel_value); +IMGUI_API void ImFontAtlasBuildMultiplyCalcLookupTable(unsigned char out_table[256], float in_multiply_factor); +IMGUI_API void ImFontAtlasBuildMultiplyRectAlpha8(const unsigned char table[256], unsigned char* pixels, int x, int y, int w, int h, int stride); //----------------------------------------------------------------------------- // [SECTION] Test Engine specific hooks (imgui_test_engine) //----------------------------------------------------------------------------- #ifdef IMGUI_ENABLE_TEST_ENGINE -extern void ImGuiTestEngineHook_ItemAdd(ImGuiContext* ctx, const ImRect& bb, ImGuiID id); -extern void ImGuiTestEngineHook_ItemInfo(ImGuiContext* ctx, ImGuiID id, const char* label, ImGuiItemStatusFlags flags); -extern void ImGuiTestEngineHook_IdInfo(ImGuiContext* ctx, ImGuiDataType data_type, ImGuiID id, const void* data_id); -extern void ImGuiTestEngineHook_IdInfo(ImGuiContext* ctx, ImGuiDataType data_type, ImGuiID id, const void* data_id, const void* data_id_end); -extern void ImGuiTestEngineHook_Log(ImGuiContext* ctx, const char* fmt, ...); +extern void ImGuiTestEngineHook_ItemAdd(ImGuiContext* ctx, const ImRect& bb, ImGuiID id); +extern void ImGuiTestEngineHook_ItemInfo(ImGuiContext* ctx, ImGuiID id, const char* label, ImGuiItemStatusFlags flags); +extern void ImGuiTestEngineHook_Log(ImGuiContext* ctx, const char* fmt, ...); +extern const char* ImGuiTestEngine_FindItemDebugLabel(ImGuiContext* ctx, ImGuiID id); + #define IMGUI_TEST_ENGINE_ITEM_ADD(_BB,_ID) if (g.TestEngineHookItems) ImGuiTestEngineHook_ItemAdd(&g, _BB, _ID) // Register item bounding box #define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) if (g.TestEngineHookItems) ImGuiTestEngineHook_ItemInfo(&g, _ID, _LABEL, _FLAGS) // Register item label and status flags (optional) #define IMGUI_TEST_ENGINE_LOG(_FMT,...) if (g.TestEngineHookItems) ImGuiTestEngineHook_Log(&g, _FMT, __VA_ARGS__) // Custom log entry from user land into test log -#define IMGUI_TEST_ENGINE_ID_INFO(_ID,_TYPE,_DATA) if (g.TestEngineHookIdInfo == id) ImGuiTestEngineHook_IdInfo(&g, _TYPE, _ID, (const void*)(_DATA)); -#define IMGUI_TEST_ENGINE_ID_INFO2(_ID,_TYPE,_DATA,_DATA2) if (g.TestEngineHookIdInfo == id) ImGuiTestEngineHook_IdInfo(&g, _TYPE, _ID, (const void*)(_DATA), (const void*)(_DATA2)); #else -#define IMGUI_TEST_ENGINE_ITEM_ADD(_BB,_ID) do { } while (0) -#define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) do { } while (0) -#define IMGUI_TEST_ENGINE_LOG(_FMT,...) do { } while (0) -#define IMGUI_TEST_ENGINE_ID_INFO(_ID,_TYPE,_DATA) do { } while (0) -#define IMGUI_TEST_ENGINE_ID_INFO2(_ID,_TYPE,_DATA,_DATA2) do { } while (0) +#define IMGUI_TEST_ENGINE_ITEM_ADD(_BB,_ID) ((void)0) +#define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) ((void)g) #endif //----------------------------------------------------------------------------- diff --git a/apps/common/external/imgui/imgui_tables.cpp b/apps/common/external/imgui/imgui_tables.cpp index 19fa625d75..27f1d5386d 100644 --- a/apps/common/external/imgui/imgui_tables.cpp +++ b/apps/common/external/imgui/imgui_tables.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.80 +// dear imgui, v1.87 // (tables and columns code) /* @@ -8,6 +8,7 @@ Index of this file: // [SECTION] Commentary // [SECTION] Header mess // [SECTION] Tables: Main code +// [SECTION] Tables: Simple accessors // [SECTION] Tables: Row changes // [SECTION] Tables: Columns changes // [SECTION] Tables: Columns width management @@ -73,7 +74,7 @@ Index of this file: // (Read carefully because this is subtle but it does make sense!) //----------------------------------------------------------------------------- // About 'outer_size': -// Its meaning needs to differ slightly depending of if we are using ScrollX/ScrollY flags. +// Its meaning needs to differ slightly depending on if we are using ScrollX/ScrollY flags. // Default value is ImVec2(0.0f, 0.0f). // X // - outer_size.x <= 0.0f -> Right-align from window/work-rect right-most edge. With -FLT_MIN or 0.0f will align exactly on right-most edge. @@ -90,7 +91,7 @@ Index of this file: // Outer size is also affected by the NoHostExtendX/NoHostExtendY flags. // Important to that note how the two flags have slightly different behaviors! // - ImGuiTableFlags_NoHostExtendX -> Make outer width auto-fit to columns (overriding outer_size.x value). Only available when ScrollX/ScrollY are disabled and Stretch columns are not used. -// - ImGuiTableFlags_NoHostExtendY -> Make outer height stop exactly at outer_size.y (prevent auto-extending table past the limit). Only available when ScrollX/ScrollY are disabled. Data below the limit will be clipped and not visible. +// - ImGuiTableFlags_NoHostExtendY -> Make outer height stop exactly at outer_size.y (prevent auto-extending table past the limit). Only available when ScrollX/ScrollY is disabled. Data below the limit will be clipped and not visible. // In theory ImGuiTableFlags_NoHostExtendY could be the default and any non-scrolling tables with outer_size.y != 0.0f would use exact height. // This would be consistent but perhaps less useful and more confusing (as vertically clipped items are not easily noticeable) //----------------------------------------------------------------------------- @@ -126,13 +127,13 @@ Index of this file: // - with Table policy ImGuiTableFlags_SizingFixedSame --> default Column policy is ImGuiTableColumnFlags_WidthFixed, default Width is max of all contents width // - with Table policy ImGuiTableFlags_SizingStretchSame --> default Column policy is ImGuiTableColumnFlags_WidthStretch, default Weight is 1.0f // - with Table policy ImGuiTableFlags_SizingStretchWeight --> default Column policy is ImGuiTableColumnFlags_WidthStretch, default Weight is proportional to contents -// Default Width and default Weight can be overriden when calling TableSetupColumn(). +// Default Width and default Weight can be overridden when calling TableSetupColumn(). //----------------------------------------------------------------------------- // About mixing Fixed/Auto and Stretch columns together: // - the typical use of mixing sizing policies is: any number of LEADING Fixed columns, followed by one or two TRAILING Stretch columns. // - using mixed policies with ScrollX does not make much sense, as using Stretch columns with ScrollX does not make much sense in the first place! -// that is, unless 'inner_width' is passed to BeginTable() to explicitely provide a total width to layout columns in. -// - when using ImGuiTableFlags_SizingFixedSame with mixed columns, only the Fixed/Auto columns will match their widths to the maximum contents width. +// that is, unless 'inner_width' is passed to BeginTable() to explicitly provide a total width to layout columns in. +// - when using ImGuiTableFlags_SizingFixedSame with mixed columns, only the Fixed/Auto columns will match their widths to the width of the maximum contents. // - when using ImGuiTableFlags_SizingStretchSame with mixed columns, only the Stretch columns will match their weight/widths. //----------------------------------------------------------------------------- // About using column width: @@ -140,9 +141,9 @@ Index of this file: // - you may use GetContentRegionAvail().x to query the width available in a given column. // - right-side alignment features such as SetNextItemWidth(-x) or PushItemWidth(-x) will rely on this width. // If the column is not resizable and has no width specified with TableSetupColumn(): -// - its width will be automatic and be the set to the max of items submitted. +// - its width will be automatic and be set to the max of items submitted. // - therefore you generally cannot have ALL items of the columns use e.g. SetNextItemWidth(-FLT_MIN). -// - but if the column has one or more item of known/fixed size, this will become the reference width used by SetNextItemWidth(-FLT_MIN). +// - but if the column has one or more items of known/fixed size, this will become the reference width used by SetNextItemWidth(-FLT_MIN). //----------------------------------------------------------------------------- @@ -161,18 +162,18 @@ Index of this file: // - Both TableSetColumnIndex() and TableNextColumn() return true when the column is visible or performing // width measurements. Otherwise, you may skip submitting the contents of a cell/column, BUT ONLY if you know // it is not going to contribute to row height. -// In many situations, you may skip submitting contents for every columns but one (e.g. the first one). +// In many situations, you may skip submitting contents for every column but one (e.g. the first one). // - Case A: column is not hidden by user, and at least partially in sight (most common case). // - Case B: column is clipped / out of sight (because of scrolling or parent ClipRect): TableNextColumn() return false as a hint but we still allow layout output. // - Case C: column is hidden explicitly by the user (e.g. via the context menu, or _DefaultHide column flag, etc.). // -// [A] [B] [C] +// [A] [B] [C] // TableNextColumn(): true false false -> [userland] when TableNextColumn() / TableSetColumnIndex() return false, user can skip submitting items but only if the column doesn't contribute to row height. // SkipItems: false false true -> [internal] when SkipItems is true, most widgets will early out if submitted, resulting is no layout output. // ClipRect: normal zero-width zero-width -> [internal] when ClipRect is zero, ItemAdd() will return false and most widgets will early out mid-way. // ImDrawList output: normal dummy dummy -> [internal] when using the dummy channel, ImDrawList submissions (if any) will be wasted (because cliprect is zero-width anyway). // -// - We need distinguish those cases because non-hidden columns that are clipped outside of scrolling bounds should still contribute their height to the row. +// - We need to distinguish those cases because non-hidden columns that are clipped outside of scrolling bounds should still contribute their height to the row. // However, in the majority of cases, the contribution to row height is the same for all columns, or the tallest cells are known by the programmer. //----------------------------------------------------------------------------- // About clipping/culling of whole Tables: @@ -209,6 +210,8 @@ Index of this file: #if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later #pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types #endif +#pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2). +#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). #endif // Clang/GCC warnings with -Weverything @@ -235,6 +238,19 @@ Index of this file: //----------------------------------------------------------------------------- // [SECTION] Tables: Main code //----------------------------------------------------------------------------- +// - TableFixFlags() [Internal] +// - TableFindByID() [Internal] +// - BeginTable() +// - BeginTableEx() [Internal] +// - TableBeginInitMemory() [Internal] +// - TableBeginApplyRequests() [Internal] +// - TableSetupColumnFlags() [Internal] +// - TableUpdateLayout() [Internal] +// - TableUpdateBorders() [Internal] +// - EndTable() +// - TableSetupColumn() +// - TableSetupScrollFreeze() +//----------------------------------------------------------------------------- // Configuration static const int TABLE_DRAW_CHANNEL_BG0 = 0; @@ -272,12 +288,7 @@ inline ImGuiTableFlags TableFixFlags(ImGuiTableFlags flags, ImGuiWindow* outer_w flags |= ImGuiTableFlags_NoSavedSettings; // Inherit _NoSavedSettings from top-level window (child windows always have _NoSavedSettings set) -#ifdef IMGUI_HAS_DOCK - ImGuiWindow* window_for_settings = outer_window->RootWindowDockStop; -#else - ImGuiWindow* window_for_settings = outer_window->RootWindow; -#endif - if (window_for_settings->Flags & ImGuiWindowFlags_NoSavedSettings) + if (outer_window->RootWindow->Flags & ImGuiWindowFlags_NoSavedSettings) flags |= ImGuiTableFlags_NoSavedSettings; return flags; @@ -313,7 +324,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG const ImVec2 avail_size = GetContentRegionAvail(); ImVec2 actual_outer_size = CalcItemSize(outer_size, ImMax(avail_size.x, 1.0f), use_child_window ? ImMax(avail_size.y, 1.0f) : 0.0f); ImRect outer_rect(outer_window->DC.CursorPos, outer_window->DC.CursorPos + actual_outer_size); - if (use_child_window && IsClippedEx(outer_rect, 0, false)) + if (use_child_window && IsClippedEx(outer_rect, 0)) { ItemSize(outer_rect); return false; @@ -327,6 +338,15 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG if (instance_no > 0) IM_ASSERT(table->ColumnsCount == columns_count && "BeginTable(): Cannot change columns count mid-frame while preserving same ID"); + // Acquire temporary buffers + const int table_idx = g.Tables.GetIndex(table); + if (++g.TablesTempDataStacked > g.TablesTempData.Size) + g.TablesTempData.resize(g.TablesTempDataStacked, ImGuiTableTempData()); + ImGuiTableTempData* temp_data = table->TempData = &g.TablesTempData[g.TablesTempDataStacked - 1]; + temp_data->TableIndex = table_idx; + table->DrawSplitter = &table->TempData->DrawSplitter; + table->DrawSplitter->Clear(); + // Fix flags table->IsDefaultSizingPolicy = (flags & ImGuiTableFlags_SizingMask_) == 0; flags = TableFixFlags(flags, outer_window); @@ -340,7 +360,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG table->ColumnsCount = columns_count; table->IsLayoutLocked = false; table->InnerWidth = inner_width; - table->UserOuterSize = outer_size; + temp_data->UserOuterSize = outer_size; // When not using a child window, WorkRect.Max will grow as we append contents. if (use_child_window) @@ -389,14 +409,14 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG table->HostIndentX = inner_window->DC.Indent.x; table->HostClipRect = inner_window->ClipRect; table->HostSkipItems = inner_window->SkipItems; - table->HostBackupWorkRect = inner_window->WorkRect; - table->HostBackupParentWorkRect = inner_window->ParentWorkRect; - table->HostBackupColumnsOffset = outer_window->DC.ColumnsOffset; - table->HostBackupPrevLineSize = inner_window->DC.PrevLineSize; - table->HostBackupCurrLineSize = inner_window->DC.CurrLineSize; - table->HostBackupCursorMaxPos = inner_window->DC.CursorMaxPos; - table->HostBackupItemWidth = outer_window->DC.ItemWidth; - table->HostBackupItemWidthStackSize = outer_window->DC.ItemWidthStack.Size; + temp_data->HostBackupWorkRect = inner_window->WorkRect; + temp_data->HostBackupParentWorkRect = inner_window->ParentWorkRect; + temp_data->HostBackupColumnsOffset = outer_window->DC.ColumnsOffset; + temp_data->HostBackupPrevLineSize = inner_window->DC.PrevLineSize; + temp_data->HostBackupCurrLineSize = inner_window->DC.CurrLineSize; + temp_data->HostBackupCursorMaxPos = inner_window->DC.CursorMaxPos; + temp_data->HostBackupItemWidth = outer_window->DC.ItemWidth; + temp_data->HostBackupItemWidthStackSize = outer_window->DC.ItemWidthStack.Size; inner_window->DC.PrevLineSize = inner_window->DC.CurrLineSize = ImVec2(0.0f, 0.0f); // Padding and Spacing @@ -439,8 +459,6 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG table->BorderColorLight = GetColorU32(ImGuiCol_TableBorderLight); // Make table current - const int table_idx = g.Tables.GetIndex(table); - g.CurrentTableStack.push_back(ImGuiPtrOrIndex(table_idx)); g.CurrentTable = table; outer_window->DC.CurrentTableIdx = table_idx; if (inner_window != outer_window) // So EndChild() within the inner window can restore the table properly. @@ -453,13 +471,18 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG if (table_idx >= g.TablesLastTimeActive.Size) g.TablesLastTimeActive.resize(table_idx + 1, -1.0f); g.TablesLastTimeActive[table_idx] = (float)g.Time; + temp_data->LastTimeActive = (float)g.Time; table->MemoryCompacted = false; // Setup memory buffer (clear data if columns count changed) - const int stored_size = table->Columns.size(); - if (stored_size != 0 && stored_size != columns_count) - { - IM_FREE(table->RawData); + ImGuiTableColumn* old_columns_to_preserve = NULL; + void* old_columns_raw_data = NULL; + const int old_columns_count = table->Columns.size(); + if (old_columns_count != 0 && old_columns_count != columns_count) + { + // Attempt to preserve width on column count change (#4046) + old_columns_to_preserve = table->Columns.Data; + old_columns_raw_data = table->RawData; table->RawData = NULL; } if (table->RawData == NULL) @@ -482,14 +505,24 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG for (int n = 0; n < columns_count; n++) { ImGuiTableColumn* column = &table->Columns[n]; - float width_auto = column->WidthAuto; - *column = ImGuiTableColumn(); - column->WidthAuto = width_auto; - column->IsPreserveWidthAuto = true; // Preserve WidthAuto when reinitializing a live table: not technically necessary but remove a visible flicker + if (old_columns_to_preserve && n < old_columns_count) + { + // FIXME: We don't attempt to preserve column order in this path. + *column = old_columns_to_preserve[n]; + } + else + { + float width_auto = column->WidthAuto; + *column = ImGuiTableColumn(); + column->WidthAuto = width_auto; + column->IsPreserveWidthAuto = true; // Preserve WidthAuto when reinitializing a live table: not technically necessary but remove a visible flicker + column->IsEnabled = column->IsUserEnabled = column->IsUserEnabledNextFrame = true; + } column->DisplayOrder = table->DisplayOrderToIndex[n] = (ImGuiTableColumnIdx)n; - column->IsEnabled = column->IsEnabledNextFrame = true; } } + if (old_columns_raw_data) + IM_FREE(old_columns_raw_data); // Load settings if (table->IsSettingsRequestLoad) @@ -530,6 +563,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG // + 0 (for ImGuiTable instance, we are pooling allocations in g.Tables) // + 1 (for table->RawData allocated below) // + 1 (for table->ColumnsNames, if names are used) +// Shared allocations per number of nested tables // + 1 (for table->Splitter._Channels) // + 2 * active_channels_count (for ImDrawCmd and ImDrawIdx buffers inside channels) // Where active_channels_count is variable but often == columns_count or columns_count + 1, see TableSetupDrawChannels() for details. @@ -538,9 +572,9 @@ void ImGui::TableBeginInitMemory(ImGuiTable* table, int columns_count) { // Allocate single buffer for our arrays ImSpanAllocator<3> span_allocator; - span_allocator.ReserveBytes(0, columns_count * sizeof(ImGuiTableColumn)); - span_allocator.ReserveBytes(1, columns_count * sizeof(ImGuiTableColumnIdx)); - span_allocator.ReserveBytes(2, columns_count * sizeof(ImGuiTableCellData)); + span_allocator.Reserve(0, columns_count * sizeof(ImGuiTableColumn)); + span_allocator.Reserve(1, columns_count * sizeof(ImGuiTableColumnIdx)); + span_allocator.Reserve(2, columns_count * sizeof(ImGuiTableCellData), 4); table->RawData = IM_ALLOC(span_allocator.GetArenaSizeInBytes()); memset(table->RawData, 0, span_allocator.GetArenaSizeInBytes()); span_allocator.SetArenaBasePtr(table->RawData); @@ -635,7 +669,7 @@ static void TableSetupColumnFlags(ImGuiTable* table, ImGuiTableColumn* column, I { IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiTableColumnFlags_WidthMask_)); // Check that only 1 of each set is used. } - + // Resize if ((table->Flags & ImGuiTableFlags_Resizable) == 0) flags |= ImGuiTableColumnFlags_NoResize; @@ -687,13 +721,14 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) table->ColumnsEnabledCount = 0; table->EnabledMaskByIndex = 0x00; table->EnabledMaskByDisplayOrder = 0x00; + table->LeftMostEnabledColumn = -1; table->MinColumnWidth = ImMax(1.0f, g.Style.FramePadding.x * 1.0f); // g.Style.ColumnsMinSpacing; // FIXME-TABLE // [Part 1] Apply/lock Enabled and Order states. Calculate auto/ideal width for columns. Count fixed/stretch columns. // Process columns in their visible orders as we are building the Prev/Next indices. int count_fixed = 0; // Number of columns that have fixed sizing policies int count_stretch = 0; // Number of columns that have stretch sizing policies - int last_visible_column_idx = -1; + int prev_visible_column_idx = -1; bool has_auto_fit_request = false; bool has_resizable = false; float stretch_sum_width_auto = 0.0f; @@ -716,16 +751,18 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) column->InitStretchWeightOrWidth = -1.0f; } - // Update Enabled state, mark settings/sortspecs dirty + // Update Enabled state, mark settings and sort specs dirty if (!(table->Flags & ImGuiTableFlags_Hideable) || (column->Flags & ImGuiTableColumnFlags_NoHide)) - column->IsEnabledNextFrame = true; - if (column->IsEnabled != column->IsEnabledNextFrame) + column->IsUserEnabledNextFrame = true; + if (column->IsUserEnabled != column->IsUserEnabledNextFrame) { - column->IsEnabled = column->IsEnabledNextFrame; + column->IsUserEnabled = column->IsUserEnabledNextFrame; table->IsSettingsDirty = true; - if (!column->IsEnabled && column->SortOrder != -1) - table->IsSortSpecsDirty = true; } + column->IsEnabled = column->IsUserEnabled && (column->Flags & ImGuiTableColumnFlags_Disabled) == 0; + + if (column->SortOrder != -1 && !column->IsEnabled) + table->IsSortSpecsDirty = true; if (column->SortOrder > 0 && !(table->Flags & ImGuiTableFlags_SortMulti)) table->IsSortSpecsDirty = true; @@ -741,14 +778,16 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) } // Mark as enabled and link to previous/next enabled column - column->PrevEnabledColumn = (ImGuiTableColumnIdx)last_visible_column_idx; + column->PrevEnabledColumn = (ImGuiTableColumnIdx)prev_visible_column_idx; column->NextEnabledColumn = -1; - if (last_visible_column_idx != -1) - table->Columns[last_visible_column_idx].NextEnabledColumn = (ImGuiTableColumnIdx)column_n; + if (prev_visible_column_idx != -1) + table->Columns[prev_visible_column_idx].NextEnabledColumn = (ImGuiTableColumnIdx)column_n; + else + table->LeftMostEnabledColumn = (ImGuiTableColumnIdx)column_n; column->IndexWithinEnabledSet = table->ColumnsEnabledCount++; table->EnabledMaskByIndex |= (ImU64)1 << column_n; table->EnabledMaskByDisplayOrder |= (ImU64)1 << column->DisplayOrder; - last_visible_column_idx = column_n; + prev_visible_column_idx = column_n; IM_ASSERT(column->IndexWithinEnabledSet <= column->DisplayOrder); // Calculate ideal/auto column width (that's the width required for all contents to be visible without clipping) @@ -778,8 +817,8 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) } if ((table->Flags & ImGuiTableFlags_Sortable) && table->SortSpecsCount == 0 && !(table->Flags & ImGuiTableFlags_SortTristate)) table->IsSortSpecsDirty = true; - table->RightMostEnabledColumn = (ImGuiTableColumnIdx)last_visible_column_idx; - IM_ASSERT(table->RightMostEnabledColumn >= 0); + table->RightMostEnabledColumn = (ImGuiTableColumnIdx)prev_visible_column_idx; + IM_ASSERT(table->LeftMostEnabledColumn >= 0 && table->RightMostEnabledColumn >= 0); // [Part 2] Disable child window clipping while fitting columns. This is not strictly necessary but makes it possible // to avoid the column fitting having to wait until the first visible frame of the child container (may or not be a good thing). @@ -805,7 +844,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) // Apply same widths policy float width_auto = column->WidthAuto; if (table_sizing_policy == ImGuiTableFlags_SizingFixedSame && (column->AutoFitQueue != 0x00 || !column_is_resizable)) - width_auto = fixed_max_width_auto; + width_auto = fixed_max_width_auto; // Apply automatic width // Latch initial size for fixed columns and update it constantly for auto-resizing column (unless clipped!) @@ -1084,7 +1123,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) // Initial state ImGuiWindow* inner_window = table->InnerWindow; if (table->Flags & ImGuiTableFlags_NoClip) - table->DrawSplitter.SetCurrentChannel(inner_window->DrawList, TABLE_DRAW_CHANNEL_NOCLIP); + table->DrawSplitter->SetCurrentChannel(inner_window->DrawList, TABLE_DRAW_CHANNEL_NOCLIP); else inner_window->DrawList->PushClipRect(inner_window->ClipRect.Min, inner_window->ClipRect.Max, false); } @@ -1122,9 +1161,8 @@ void ImGui::TableUpdateBorders(ImGuiTable* table) if ((table->Flags & ImGuiTableFlags_NoBordersInBody) && table->IsUsingHeaders == false) continue; - if (table->FreezeColumnsCount > 0) - if (column->MaxX < table->Columns[table->DisplayOrderToIndex[table->FreezeColumnsCount - 1]].MaxX) - continue; + if (!column->IsVisibleX && table->LastResizedColumn != column_n) + continue; ImGuiID column_id = TableGetColumnResizeID(table, column_n, table->InstanceCurrent); ImRect hit_rect(column->MaxX - hit_half_width, hit_y1, column->MaxX + hit_half_width, border_y2_hit); @@ -1132,7 +1170,7 @@ void ImGui::TableUpdateBorders(ImGuiTable* table) KeepAliveID(column_id); bool hovered = false, held = false; - bool pressed = ButtonBehavior(hit_rect, column_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowItemOverlap | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick); + bool pressed = ButtonBehavior(hit_rect, column_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowItemOverlap | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick | ImGuiButtonFlags_NoNavFocus); if (pressed && IsMouseDoubleClicked(0)) { TableSetColumnWidthAutoSingle(table, column_n); @@ -1172,6 +1210,7 @@ void ImGui::EndTable() const ImGuiTableFlags flags = table->Flags; ImGuiWindow* inner_window = table->InnerWindow; ImGuiWindow* outer_window = table->OuterWindow; + ImGuiTableTempData* temp_data = table->TempData; IM_ASSERT(inner_window == g.CurrentWindow); IM_ASSERT(outer_window == inner_window || outer_window == inner_window->ParentWindow); @@ -1184,9 +1223,9 @@ void ImGui::EndTable() TableOpenContextMenu((int)table->HoveredColumnBody); // Finalize table height - inner_window->DC.PrevLineSize = table->HostBackupPrevLineSize; - inner_window->DC.CurrLineSize = table->HostBackupCurrLineSize; - inner_window->DC.CursorMaxPos = table->HostBackupCursorMaxPos; + inner_window->DC.PrevLineSize = temp_data->HostBackupPrevLineSize; + inner_window->DC.CurrLineSize = temp_data->HostBackupCurrLineSize; + inner_window->DC.CursorMaxPos = temp_data->HostBackupCursorMaxPos; const float inner_content_max_y = table->RowPosY2; IM_ASSERT(table->RowPosY2 == inner_window->DC.CursorPos.y); if (inner_window != outer_window) @@ -1233,10 +1272,11 @@ void ImGui::EndTable() #endif // Flatten channels and merge draw calls - table->DrawSplitter.SetCurrentChannel(inner_window->DrawList, 0); + ImDrawListSplitter* splitter = table->DrawSplitter; + splitter->SetCurrentChannel(inner_window->DrawList, 0); if ((table->Flags & ImGuiTableFlags_NoClip) == 0) TableMergeDrawChannels(table); - table->DrawSplitter.Merge(inner_window->DrawList); + splitter->Merge(inner_window->DrawList); // Update ColumnsAutoFitWidth to get us ahead for host using our size to auto-resize without waiting for next BeginTable() const float width_spacings = (table->OuterPaddingX * 2.0f) + (table->CellSpacingX1 + table->CellSpacingX2) * (table->ColumnsEnabledCount - 1); @@ -1278,18 +1318,18 @@ void ImGui::EndTable() // Pop from id stack IM_ASSERT_USER_ERROR(inner_window->IDStack.back() == table->ID + table->InstanceCurrent, "Mismatching PushID/PopID!"); - IM_ASSERT_USER_ERROR(outer_window->DC.ItemWidthStack.Size >= table->HostBackupItemWidthStackSize, "Too many PopItemWidth!"); + IM_ASSERT_USER_ERROR(outer_window->DC.ItemWidthStack.Size >= temp_data->HostBackupItemWidthStackSize, "Too many PopItemWidth!"); PopID(); // Restore window data that we modified const ImVec2 backup_outer_max_pos = outer_window->DC.CursorMaxPos; - inner_window->WorkRect = table->HostBackupWorkRect; - inner_window->ParentWorkRect = table->HostBackupParentWorkRect; + inner_window->WorkRect = temp_data->HostBackupWorkRect; + inner_window->ParentWorkRect = temp_data->HostBackupParentWorkRect; inner_window->SkipItems = table->HostSkipItems; outer_window->DC.CursorPos = table->OuterRect.Min; - outer_window->DC.ItemWidth = table->HostBackupItemWidth; - outer_window->DC.ItemWidthStack.Size = table->HostBackupItemWidthStackSize; - outer_window->DC.ColumnsOffset = table->HostBackupColumnsOffset; + outer_window->DC.ItemWidth = temp_data->HostBackupItemWidth; + outer_window->DC.ItemWidthStack.Size = temp_data->HostBackupItemWidthStackSize; + outer_window->DC.ColumnsOffset = temp_data->HostBackupColumnsOffset; // Layout in outer window // (FIXME: To allow auto-fit and allow desirable effect of SameLine() we dissociate 'used' vs 'ideal' size by overriding @@ -1312,20 +1352,20 @@ void ImGui::EndTable() IM_ASSERT((table->Flags & ImGuiTableFlags_ScrollX) == 0); outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, table->OuterRect.Min.x + table->ColumnsAutoFitWidth); } - else if (table->UserOuterSize.x <= 0.0f) + else if (temp_data->UserOuterSize.x <= 0.0f) { const float decoration_size = (table->Flags & ImGuiTableFlags_ScrollX) ? inner_window->ScrollbarSizes.x : 0.0f; - outer_window->DC.IdealMaxPos.x = ImMax(outer_window->DC.IdealMaxPos.x, table->OuterRect.Min.x + table->ColumnsAutoFitWidth + decoration_size - table->UserOuterSize.x); + outer_window->DC.IdealMaxPos.x = ImMax(outer_window->DC.IdealMaxPos.x, table->OuterRect.Min.x + table->ColumnsAutoFitWidth + decoration_size - temp_data->UserOuterSize.x); outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, ImMin(table->OuterRect.Max.x, table->OuterRect.Min.x + table->ColumnsAutoFitWidth)); } else { outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, table->OuterRect.Max.x); } - if (table->UserOuterSize.y <= 0.0f) + if (temp_data->UserOuterSize.y <= 0.0f) { const float decoration_size = (table->Flags & ImGuiTableFlags_ScrollY) ? inner_window->ScrollbarSizes.y : 0.0f; - outer_window->DC.IdealMaxPos.y = ImMax(outer_window->DC.IdealMaxPos.y, inner_content_max_y + decoration_size - table->UserOuterSize.y); + outer_window->DC.IdealMaxPos.y = ImMax(outer_window->DC.IdealMaxPos.y, inner_content_max_y + decoration_size - temp_data->UserOuterSize.y); outer_window->DC.CursorMaxPos.y = ImMax(backup_outer_max_pos.y, ImMin(table->OuterRect.Max.y, inner_content_max_y)); } else @@ -1341,8 +1381,14 @@ void ImGui::EndTable() // Clear or restore current table, if any IM_ASSERT(g.CurrentWindow == outer_window && g.CurrentTable == table); - g.CurrentTableStack.pop_back(); - g.CurrentTable = g.CurrentTableStack.Size ? g.Tables.GetByIndex(g.CurrentTableStack.back().Index) : NULL; + IM_ASSERT(g.TablesTempDataStacked > 0); + temp_data = (--g.TablesTempDataStacked > 0) ? &g.TablesTempData[g.TablesTempDataStacked - 1] : NULL; + g.CurrentTable = temp_data ? g.Tables.GetByIndex(temp_data->TableIndex) : NULL; + if (g.CurrentTable) + { + g.CurrentTable->TempData = temp_data; + g.CurrentTable->DrawSplitter = &temp_data->DrawSplitter; + } outer_window->DC.CurrentTableIdx = g.CurrentTable ? g.Tables.GetIndex(g.CurrentTable) : -1; } @@ -1367,7 +1413,7 @@ void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, flo // Assert when passing a width or weight if policy is entirely left to default, to avoid storing width into weight and vice-versa. // Give a grace to users of ImGuiTableFlags_ScrollX. if (table->IsDefaultSizingPolicy && (flags & ImGuiTableColumnFlags_WidthMask_) == 0 && (flags & ImGuiTableFlags_ScrollX) == 0) - IM_ASSERT(init_width_or_weight <= 0.0f && "Can only specify width/weight if sizing policy is set explicitely in either Table or Column."); + IM_ASSERT(init_width_or_weight <= 0.0f && "Can only specify width/weight if sizing policy is set explicitly in either Table or Column."); // When passing a width automatically enforce WidthFixed policy // (whereas TableSetupColumnFlags would default to WidthAuto if table is not Resizable) @@ -1398,7 +1444,7 @@ void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, flo // Init default visibility/sort state if ((flags & ImGuiTableColumnFlags_DefaultHide) && (table->SettingsLoadedFlags & ImGuiTableFlags_Hideable) == 0) - column->IsEnabled = column->IsEnabledNextFrame = false; + column->IsUserEnabled = column->IsUserEnabledNextFrame = false; if (flags & ImGuiTableColumnFlags_DefaultSort && (table->SettingsLoadedFlags & ImGuiTableFlags_Sortable) == 0) { column->SortOrder = 0; // Multiple columns using _DefaultSort will be reassigned unique SortOrder values when building the sort specs. @@ -1425,13 +1471,39 @@ void ImGui::TableSetupScrollFreeze(int columns, int rows) IM_ASSERT(columns >= 0 && columns < IMGUI_TABLE_MAX_COLUMNS); IM_ASSERT(rows >= 0 && rows < 128); // Arbitrary limit - table->FreezeColumnsRequest = (table->Flags & ImGuiTableFlags_ScrollX) ? (ImGuiTableColumnIdx)columns : 0; + table->FreezeColumnsRequest = (table->Flags & ImGuiTableFlags_ScrollX) ? (ImGuiTableColumnIdx)ImMin(columns, table->ColumnsCount) : 0; table->FreezeColumnsCount = (table->InnerWindow->Scroll.x != 0.0f) ? table->FreezeColumnsRequest : 0; table->FreezeRowsRequest = (table->Flags & ImGuiTableFlags_ScrollY) ? (ImGuiTableColumnIdx)rows : 0; table->FreezeRowsCount = (table->InnerWindow->Scroll.y != 0.0f) ? table->FreezeRowsRequest : 0; table->IsUnfrozenRows = (table->FreezeRowsCount == 0); // Make sure this is set before TableUpdateLayout() so ImGuiListClipper can benefit from it.b + + // Ensure frozen columns are ordered in their section. We still allow multiple frozen columns to be reordered. + // FIXME-TABLE: This work for preserving 2143 into 21|43. How about 4321 turning into 21|43? (preserve relative order in each section) + for (int column_n = 0; column_n < table->FreezeColumnsRequest; column_n++) + { + int order_n = table->DisplayOrderToIndex[column_n]; + if (order_n != column_n && order_n >= table->FreezeColumnsRequest) + { + ImSwap(table->Columns[table->DisplayOrderToIndex[order_n]].DisplayOrder, table->Columns[table->DisplayOrderToIndex[column_n]].DisplayOrder); + ImSwap(table->DisplayOrderToIndex[order_n], table->DisplayOrderToIndex[column_n]); + } + } } +//----------------------------------------------------------------------------- +// [SECTION] Tables: Simple accessors +//----------------------------------------------------------------------------- +// - TableGetColumnCount() +// - TableGetColumnName() +// - TableGetColumnName() [Internal] +// - TableSetColumnEnabled() +// - TableGetColumnFlags() +// - TableGetCellBgRect() [Internal] +// - TableGetColumnResizeID() [Internal] +// - TableGetHoveredColumn() [Internal] +// - TableSetBgColor() +//----------------------------------------------------------------------------- + int ImGui::TableGetColumnCount() { ImGuiContext& g = *GImGui; @@ -1460,6 +1532,27 @@ const char* ImGui::TableGetColumnName(const ImGuiTable* table, int column_n) return &table->ColumnsNames.Buf[column->NameOffset]; } +// Change user accessible enabled/disabled state of a column (often perceived as "showing/hiding" from users point of view) +// Note that end-user can use the context menu to change this themselves (right-click in headers, or right-click in columns body with ImGuiTableFlags_ContextMenuInBody) +// - Require table to have the ImGuiTableFlags_Hideable flag because we are manipulating user accessible state. +// - Request will be applied during next layout, which happens on the first call to TableNextRow() after BeginTable(). +// - For the getter you can test (TableGetColumnFlags() & ImGuiTableColumnFlags_IsEnabled) != 0. +// - Alternative: the ImGuiTableColumnFlags_Disabled is an overriding/master disable flag which will also hide the column from context menu. +void ImGui::TableSetColumnEnabled(int column_n, bool enabled) +{ + ImGuiContext& g = *GImGui; + ImGuiTable* table = g.CurrentTable; + IM_ASSERT(table != NULL); + if (!table) + return; + IM_ASSERT(table->Flags & ImGuiTableFlags_Hideable); // See comments above + if (column_n < 0) + column_n = table->CurrentColumn; + IM_ASSERT(column_n >= 0 && column_n < table->ColumnsCount); + ImGuiTableColumn* column = &table->Columns[column_n]; + column->IsUserEnabledNextFrame = enabled; +} + // We allow querying for an extra column in order to poll the IsHovered state of the right-most section ImGuiTableColumnFlags ImGui::TableGetColumnFlags(int column_n) { @@ -1476,18 +1569,21 @@ ImGuiTableColumnFlags ImGui::TableGetColumnFlags(int column_n) // Return the cell rectangle based on currently known height. // - Important: we generally don't know our row height until the end of the row, so Max.y will be incorrect in many situations. -// The only case where this is correct is if we provided a min_row_height to TableNextRow() and don't go below it. +// The only case where this is correct is if we provided a min_row_height to TableNextRow() and don't go below it, or in TableEndRow() when we locked that height. // - Important: if ImGuiTableFlags_PadOuterX is set but ImGuiTableFlags_PadInnerX is not set, the outer-most left and right // columns report a small offset so their CellBgRect can extend up to the outer border. +// FIXME: But the rendering code in TableEndRow() nullifies that with clamping required for scrolling. ImRect ImGui::TableGetCellBgRect(const ImGuiTable* table, int column_n) { const ImGuiTableColumn* column = &table->Columns[column_n]; float x1 = column->MinX; float x2 = column->MaxX; - if (column->PrevEnabledColumn == -1) - x1 -= table->CellSpacingX1; - if (column->NextEnabledColumn == -1) - x2 += table->CellSpacingX2; + //if (column->PrevEnabledColumn == -1) + // x1 -= table->OuterPaddingX; + //if (column->NextEnabledColumn == -1) + // x2 += table->OuterPaddingX; + x1 = ImMax(x1, table->WorkRect.Min.x); + x2 = ImMin(x2, table->WorkRect.Max.x); return ImRect(x1, table->RowPosY1, x2, table->RowPosY2); } @@ -1639,6 +1735,10 @@ void ImGui::TableEndRow(ImGuiTable* table) if (table->CurrentColumn != -1) TableEndCell(table); + // Logging + if (g.LogEnabled) + LogRenderedText(NULL, "|"); + // Position cursor at the bottom of our row so it can be used for e.g. clipping calculation. However it is // likely that the next call to TableBeginCell() will reposition the cursor to take account of vertical padding. window->DC.CursorPos.y = table->RowPosY2; @@ -1679,7 +1779,7 @@ void ImGui::TableEndRow(ImGuiTable* table) // always followed by a change of clipping rectangle we perform the smallest overwrite possible here. if ((table->Flags & ImGuiTableFlags_NoClip) == 0) window->DrawList->_CmdHeader.ClipRect = table->Bg0ClipRectForDrawCmd.ToVec4(); - table->DrawSplitter.SetCurrentChannel(window->DrawList, TABLE_DRAW_CHANNEL_BG0); + table->DrawSplitter->SetCurrentChannel(window->DrawList, TABLE_DRAW_CHANNEL_BG0); } // Draw row background @@ -1700,10 +1800,12 @@ void ImGui::TableEndRow(ImGuiTable* table) ImGuiTableCellData* cell_data_end = &table->RowCellData[table->RowCellDataCurrent]; for (ImGuiTableCellData* cell_data = &table->RowCellData[0]; cell_data <= cell_data_end; cell_data++) { + // As we render the BG here we need to clip things (for layout we would not) + // FIXME: This cancels the OuterPadding addition done by TableGetCellBgRect(), need to keep it while rendering correctly while scrolling. const ImGuiTableColumn* column = &table->Columns[cell_data->Column]; ImRect cell_bg_rect = TableGetCellBgRect(table, cell_data->Column); cell_bg_rect.ClipWith(table->BgClipRect); - cell_bg_rect.Min.x = ImMax(cell_bg_rect.Min.x, column->ClipRect.Min.x); // So that first column after frozen one gets clipped + cell_bg_rect.Min.x = ImMax(cell_bg_rect.Min.x, column->ClipRect.Min.x); // So that first column after frozen one gets clipped when scrolling cell_bg_rect.Max.x = ImMin(cell_bg_rect.Max.x, column->MaxX); window->DrawList->AddRectFilled(cell_bg_rect.Min, cell_bg_rect.Max, cell_data->BgColor); } @@ -1751,7 +1853,7 @@ void ImGui::TableEndRow(ImGuiTable* table) // Update cliprect ahead of TableBeginCell() so clipper can access to new ClipRect->Min.y SetWindowClipRectBeforeSetChannel(window, table->Columns[0].ClipRect); - table->DrawSplitter.SetCurrentChannel(window->DrawList, table->Columns[0].DrawChannelCurrent); + table->DrawSplitter->SetCurrentChannel(window->DrawList, table->Columns[0].DrawChannelCurrent); } if (!(table->RowFlags & ImGuiTableRowFlags_Headers)) @@ -1859,21 +1961,30 @@ void ImGui::TableBeginCell(ImGuiTable* table, int column_n) window->SkipItems = column->IsSkipItems; if (column->IsSkipItems) { - window->DC.LastItemId = 0; - window->DC.LastItemStatusFlags = 0; + ImGuiContext& g = *GImGui; + g.LastItemData.ID = 0; + g.LastItemData.StatusFlags = 0; } if (table->Flags & ImGuiTableFlags_NoClip) { // FIXME: if we end up drawing all borders/bg in EndTable, could remove this and just assert that channel hasn't changed. - table->DrawSplitter.SetCurrentChannel(window->DrawList, TABLE_DRAW_CHANNEL_NOCLIP); + table->DrawSplitter->SetCurrentChannel(window->DrawList, TABLE_DRAW_CHANNEL_NOCLIP); //IM_ASSERT(table->DrawSplitter._Current == TABLE_DRAW_CHANNEL_NOCLIP); } else { // FIXME-TABLE: Could avoid this if draw channel is dummy channel? SetWindowClipRectBeforeSetChannel(window, column->ClipRect); - table->DrawSplitter.SetCurrentChannel(window->DrawList, column->DrawChannelCurrent); + table->DrawSplitter->SetCurrentChannel(window->DrawList, column->DrawChannelCurrent); + } + + // Logging + ImGuiContext& g = *GImGui; + if (g.LogEnabled && !column->IsSkipItems) + { + LogRenderedText(&window->DC.CursorPos, "|"); + g.LogLinePosY = FLT_MAX; } } @@ -1918,6 +2029,7 @@ float ImGui::TableGetMaxColumnWidth(const ImGuiTable* table, int column_n) if (table->Flags & ImGuiTableFlags_ScrollX) { // Frozen columns can't reach beyond visible width else scrolling will naturally break. + // (we use DisplayOrder as within a set of multiple frozen column reordering is possible) if (column->DisplayOrder < table->FreezeColumnsRequest) { max_width = (table->InnerClipRect.Max.x - (table->FreezeColumnsRequest - column->DisplayOrder) * min_column_distance) - column->MinX; @@ -2113,7 +2225,7 @@ void ImGui::TablePushBackgroundChannel() // Optimization: avoid SetCurrentChannel() + PushClipRect() table->HostBackupInnerClipRect = window->ClipRect; SetWindowClipRectBeforeSetChannel(window, table->Bg2ClipRectForDrawCmd); - table->DrawSplitter.SetCurrentChannel(window->DrawList, table->Bg2DrawChannelCurrent); + table->DrawSplitter->SetCurrentChannel(window->DrawList, table->Bg2DrawChannelCurrent); } void ImGui::TablePopBackgroundChannel() @@ -2125,7 +2237,7 @@ void ImGui::TablePopBackgroundChannel() // Optimization: avoid PopClipRect() + SetCurrentChannel() SetWindowClipRectBeforeSetChannel(window, table->HostBackupInnerClipRect); - table->DrawSplitter.SetCurrentChannel(window->DrawList, column->DrawChannelCurrent); + table->DrawSplitter->SetCurrentChannel(window->DrawList, column->DrawChannelCurrent); } // Allocate draw channels. Called by TableUpdateLayout() @@ -2151,7 +2263,7 @@ void ImGui::TableSetupDrawChannels(ImGuiTable* table) const int channels_for_bg = 1 + 1 * freeze_row_multiplier; const int channels_for_dummy = (table->ColumnsEnabledCount < table->ColumnsCount || table->VisibleMaskByIndex != table->EnabledMaskByIndex) ? +1 : 0; const int channels_total = channels_for_bg + (channels_for_row * freeze_row_multiplier) + channels_for_dummy; - table->DrawSplitter.Split(table->InnerWindow->DrawList, channels_total); + table->DrawSplitter->Split(table->InnerWindow->DrawList, channels_total); table->DummyDrawChannel = (ImGuiTableDrawChannelIdx)((channels_for_dummy > 0) ? channels_total - 1 : -1); table->Bg2DrawChannelCurrent = TABLE_DRAW_CHANNEL_BG2_FROZEN; table->Bg2DrawChannelUnfrozen = (ImGuiTableDrawChannelIdx)((table->FreezeRowsCount > 0) ? 2 + channels_for_row : TABLE_DRAW_CHANNEL_BG2_FROZEN); @@ -2215,7 +2327,7 @@ void ImGui::TableSetupDrawChannels(ImGuiTable* table) void ImGui::TableMergeDrawChannels(ImGuiTable* table) { ImGuiContext& g = *GImGui; - ImDrawListSplitter* splitter = &table->DrawSplitter; + ImDrawListSplitter* splitter = table->DrawSplitter; const bool has_freeze_v = (table->FreezeRowsCount > 0); const bool has_freeze_h = (table->FreezeColumnsCount > 0); IM_ASSERT(splitter->_Current == 0); @@ -2226,10 +2338,11 @@ void ImGui::TableMergeDrawChannels(ImGuiTable* table) ImRect ClipRect; int ChannelsCount; ImBitArray ChannelsMask; + + MergeGroup() { ChannelsCount = 0; } }; int merge_group_mask = 0x00; MergeGroup merge_groups[4]; - memset(merge_groups, 0, sizeof(merge_groups)); // 1. Scan channels and take note of those which can be merged for (int column_n = 0; column_n < table->ColumnsCount; column_n++) @@ -2245,7 +2358,7 @@ void ImGui::TableMergeDrawChannels(ImGuiTable* table) // Don't attempt to merge if there are multiple draw calls within the column ImDrawChannel* src_channel = &splitter->_Channels[channel_no]; - if (src_channel->_CmdBuffer.Size > 0 && src_channel->_CmdBuffer.back().ElemCount == 0) + if (src_channel->_CmdBuffer.Size > 0 && src_channel->_CmdBuffer.back().ElemCount == 0 && src_channel->_CmdBuffer.back().UserCallback != NULL) // Equivalent of PopUnusedDrawCmd() src_channel->_CmdBuffer.pop_back(); if (src_channel->_CmdBuffer.Size != 1) continue; @@ -2307,7 +2420,6 @@ void ImGui::TableMergeDrawChannels(ImGuiTable* table) g.DrawChannelsTempMergeBuffer.resize(splitter->_Count - LEADING_DRAW_CHANNELS); // Use shared temporary storage so the allocation gets amortized ImDrawChannel* dst_tmp = g.DrawChannelsTempMergeBuffer.Data; ImBitArray remaining_mask; // We need 132-bit of storage - remaining_mask.ClearAllBits(); remaining_mask.SetBitRange(LEADING_DRAW_CHANNELS, splitter->_Count); remaining_mask.ClearBit(table->Bg2DrawChannelUnfrozen); IM_ASSERT(has_freeze_v == false || table->Bg2DrawChannelUnfrozen != TABLE_DRAW_CHANNEL_BG2_FROZEN); @@ -2337,7 +2449,7 @@ void ImGui::TableMergeDrawChannels(ImGuiTable* table) if ((merge_group_n & 2) != 0 && (table->Flags & ImGuiTableFlags_NoHostExtendY) == 0) merge_clip_rect.Max.y = ImMax(merge_clip_rect.Max.y, host_rect.Max.y); #if 0 - GetOverlayDrawList()->AddRect(merge_group->ClipRect.Min, merge_group->ClipRect.Max, IM_COL32(255, 0, 0, 200), 0.0f, ~0, 1.0f); + GetOverlayDrawList()->AddRect(merge_group->ClipRect.Min, merge_group->ClipRect.Max, IM_COL32(255, 0, 0, 200), 0.0f, 0, 1.0f); GetOverlayDrawList()->AddLine(merge_group->ClipRect.Min, merge_clip_rect.Min, IM_COL32(255, 100, 0, 200)); GetOverlayDrawList()->AddLine(merge_group->ClipRect.Max, merge_clip_rect.Max, IM_COL32(255, 100, 0, 200)); #endif @@ -2386,7 +2498,7 @@ void ImGui::TableDrawBorders(ImGuiTable* table) return; ImDrawList* inner_drawlist = inner_window->DrawList; - table->DrawSplitter.SetCurrentChannel(inner_drawlist, TABLE_DRAW_CHANNEL_BG0); + table->DrawSplitter->SetCurrentChannel(inner_drawlist, TABLE_DRAW_CHANNEL_BG0); inner_drawlist->PushClipRect(table->Bg0ClipRectForDrawCmd.Min, table->Bg0ClipRectForDrawCmd.Max, false); // Draw inner border and resizing feedback @@ -2406,7 +2518,7 @@ void ImGui::TableDrawBorders(ImGuiTable* table) const bool is_hovered = (table->HoveredColumnBorder == column_n); const bool is_resized = (table->ResizedColumn == column_n) && (table->InstanceInteracted == table->InstanceCurrent); const bool is_resizable = (column->Flags & (ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_NoDirectResize_)) == 0; - const bool is_frozen_separator = (table->FreezeColumnsCount != -1 && table->FreezeColumnsCount == order_n + 1); + const bool is_frozen_separator = (table->FreezeColumnsCount == order_n + 1); if (column->MaxX > table->InnerClipRect.Max.x && !is_resized) continue; @@ -2450,7 +2562,7 @@ void ImGui::TableDrawBorders(ImGuiTable* table) const ImU32 outer_col = table->BorderColorStrong; if ((table->Flags & ImGuiTableFlags_BordersOuter) == ImGuiTableFlags_BordersOuter) { - inner_drawlist->AddRect(outer_border.Min, outer_border.Max, outer_col, 0.0f, ~0, border_size); + inner_drawlist->AddRect(outer_border.Min, outer_border.Max, outer_col, 0.0f, 0, border_size); } else if (table->Flags & ImGuiTableFlags_BordersOuterV) { @@ -2502,8 +2614,7 @@ ImGuiTableSortSpecs* ImGui::TableGetSortSpecs() if (!table->IsLayoutLocked) TableUpdateLayout(table); - if (table->IsSortSpecsDirty) - TableSortSpecsBuild(table); + TableSortSpecsBuild(table); return &table->SortSpecs; } @@ -2642,28 +2753,33 @@ void ImGui::TableSortSpecsSanitize(ImGuiTable* table) void ImGui::TableSortSpecsBuild(ImGuiTable* table) { - IM_ASSERT(table->IsSortSpecsDirty); - TableSortSpecsSanitize(table); + bool dirty = table->IsSortSpecsDirty; + if (dirty) + { + TableSortSpecsSanitize(table); + table->SortSpecsMulti.resize(table->SortSpecsCount <= 1 ? 0 : table->SortSpecsCount); + table->SortSpecs.SpecsDirty = true; // Mark as dirty for user + table->IsSortSpecsDirty = false; // Mark as not dirty for us + } // Write output - table->SortSpecsMulti.resize(table->SortSpecsCount <= 1 ? 0 : table->SortSpecsCount); ImGuiTableColumnSortSpecs* sort_specs = (table->SortSpecsCount == 0) ? NULL : (table->SortSpecsCount == 1) ? &table->SortSpecsSingle : table->SortSpecsMulti.Data; - for (int column_n = 0; column_n < table->ColumnsCount; column_n++) - { - ImGuiTableColumn* column = &table->Columns[column_n]; - if (column->SortOrder == -1) - continue; - IM_ASSERT(column->SortOrder < table->SortSpecsCount); - ImGuiTableColumnSortSpecs* sort_spec = &sort_specs[column->SortOrder]; - sort_spec->ColumnUserID = column->UserID; - sort_spec->ColumnIndex = (ImGuiTableColumnIdx)column_n; - sort_spec->SortOrder = (ImGuiTableColumnIdx)column->SortOrder; - sort_spec->SortDirection = column->SortDirection; - } + if (dirty && sort_specs != NULL) + for (int column_n = 0; column_n < table->ColumnsCount; column_n++) + { + ImGuiTableColumn* column = &table->Columns[column_n]; + if (column->SortOrder == -1) + continue; + IM_ASSERT(column->SortOrder < table->SortSpecsCount); + ImGuiTableColumnSortSpecs* sort_spec = &sort_specs[column->SortOrder]; + sort_spec->ColumnUserID = column->UserID; + sort_spec->ColumnIndex = (ImGuiTableColumnIdx)column_n; + sort_spec->SortOrder = (ImGuiTableColumnIdx)column->SortOrder; + sort_spec->SortDirection = column->SortDirection; + } + table->SortSpecs.Specs = sort_specs; table->SortSpecs.SpecsCount = table->SortSpecsCount; - table->SortSpecs.SpecsDirty = true; // Mark as dirty for user - table->IsSortSpecsDirty = false; // Mark as not dirty for us } //------------------------------------------------------------------------- @@ -2683,8 +2799,11 @@ float ImGui::TableGetHeaderRowHeight() float row_height = GetTextLineHeight(); int columns_count = TableGetColumnCount(); for (int column_n = 0; column_n < columns_count; column_n++) - if (TableGetColumnFlags(column_n) & ImGuiTableColumnFlags_IsEnabled) + { + ImGuiTableColumnFlags flags = TableGetColumnFlags(column_n); + if ((flags & ImGuiTableColumnFlags_IsEnabled) && !(flags & ImGuiTableColumnFlags_NoHeaderLabel)) row_height = ImMax(row_height, CalcTextSize(TableGetColumnName(column_n)).y); + } row_height += GetStyle().CellPadding.y * 2.0f; return row_height; } @@ -2721,7 +2840,7 @@ void ImGui::TableHeadersRow() // Push an id to allow unnamed labels (generally accidental, but let's behave nicely with them) // - in your own code you may omit the PushID/PopID all-together, provided you know they won't collide // - table->InstanceCurrent is only >0 when we use multiple BeginTable/EndTable calls with same identifier. - const char* name = TableGetColumnName(column_n); + const char* name = (TableGetColumnFlags(column_n) & ImGuiTableColumnFlags_NoHeaderLabel) ? "" : TableGetColumnName(column_n); PushID(table->InstanceCurrent * table->ColumnsCount + column_n); TableHeader(name); PopID(); @@ -2803,7 +2922,6 @@ void ImGui::TableHeader(const char* label) const ImU32 col = GetColorU32(held ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); //RenderFrame(bb.Min, bb.Max, col, false, 0.0f); TableSetBgColor(ImGuiTableBgTarget_CellBg, col, table->CurrentColumn); - RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding); } else { @@ -2811,6 +2929,7 @@ void ImGui::TableHeader(const char* label) if ((table->RowFlags & ImGuiTableRowFlags_Headers) == 0) TableSetBgColor(ImGuiTableBgTarget_CellBg, GetColorU32(ImGuiCol_TableHeaderBg), table->CurrentColumn); } + RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding); if (held) table->HeldHeaderColumn = (ImGuiTableColumnIdx)column_n; window->DC.CursorPos.y -= g.Style.ItemSpacing.y * 0.5f; @@ -2976,16 +3095,19 @@ void ImGui::TableDrawContextMenu(ImGuiTable* table) for (int other_column_n = 0; other_column_n < table->ColumnsCount; other_column_n++) { ImGuiTableColumn* other_column = &table->Columns[other_column_n]; + if (other_column->Flags & ImGuiTableColumnFlags_Disabled) + continue; + const char* name = TableGetColumnName(table, other_column_n); if (name == NULL || name[0] == 0) name = ""; // Make sure we can't hide the last active column bool menu_item_active = (other_column->Flags & ImGuiTableColumnFlags_NoHide) ? false : true; - if (other_column->IsEnabled && table->ColumnsEnabledCount <= 1) + if (other_column->IsUserEnabled && table->ColumnsEnabledCount <= 1) menu_item_active = false; - if (MenuItem(name, NULL, other_column->IsEnabled, menu_item_active)) - other_column->IsEnabledNextFrame = !other_column->IsEnabled; + if (MenuItem(name, NULL, other_column->IsUserEnabled, menu_item_active)) + other_column->IsUserEnabledNextFrame = !other_column->IsUserEnabled; } PopItemFlag(); } @@ -3110,7 +3232,7 @@ void ImGui::TableSaveSettings(ImGuiTable* table) column_settings->DisplayOrder = column->DisplayOrder; column_settings->SortOrder = column->SortOrder; column_settings->SortDirection = column->SortDirection; - column_settings->IsEnabled = column->IsEnabled; + column_settings->IsEnabled = column->IsUserEnabled; column_settings->IsStretch = (column->Flags & ImGuiTableColumnFlags_WidthStretch) ? 1 : 0; if ((column->Flags & ImGuiTableColumnFlags_WidthStretch) == 0) save_ref_scale = true; @@ -3124,7 +3246,7 @@ void ImGui::TableSaveSettings(ImGuiTable* table) settings->SaveFlags |= ImGuiTableFlags_Reorderable; if (column->SortOrder != -1) settings->SaveFlags |= ImGuiTableFlags_Sortable; - if (column->IsEnabled != ((column->Flags & ImGuiTableColumnFlags_DefaultHide) == 0)) + if (column->IsUserEnabled != ((column->Flags & ImGuiTableColumnFlags_DefaultHide) == 0)) settings->SaveFlags |= ImGuiTableFlags_Hideable; } settings->SaveFlags &= table->Flags; @@ -3182,7 +3304,7 @@ void ImGui::TableLoadSettings(ImGuiTable* table) else column->DisplayOrder = (ImGuiTableColumnIdx)column_n; display_order_mask |= (ImU64)1 << column->DisplayOrder; - column->IsEnabled = column->IsEnabledNextFrame = column_settings->IsEnabled; + column->IsUserEnabled = column->IsUserEnabledNextFrame = column_settings->IsEnabled; column->SortOrder = column_settings->SortOrder; column->SortDirection = column_settings->SortDirection; } @@ -3201,8 +3323,9 @@ void ImGui::TableLoadSettings(ImGuiTable* table) static void TableSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*) { ImGuiContext& g = *ctx; - for (int i = 0; i != g.Tables.GetSize(); i++) - g.Tables.GetByIndex(i)->SettingsOffset = -1; + for (int i = 0; i != g.Tables.GetMapSize(); i++) + if (ImGuiTable* table = g.Tables.TryGetMapData(i)) + table->SettingsOffset = -1; g.SettingsTables.clear(); } @@ -3210,12 +3333,12 @@ static void TableSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandle static void TableSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandler*) { ImGuiContext& g = *ctx; - for (int i = 0; i != g.Tables.GetSize(); i++) - { - ImGuiTable* table = g.Tables.GetByIndex(i); - table->IsSettingsRequestLoad = true; - table->SettingsOffset = -1; - } + for (int i = 0; i != g.Tables.GetMapSize(); i++) + if (ImGuiTable* table = g.Tables.TryGetMapData(i)) + { + table->IsSettingsRequestLoad = true; + table->SettingsOffset = -1; + } } static void* TableSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name) @@ -3288,6 +3411,9 @@ static void TableSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandle for (int column_n = 0; column_n < settings->ColumnsCount; column_n++, column++) { // "Column 0 UserID=0x42AD2D21 Width=100 Visible=1 Order=0 Sort=0v" + bool save_column = column->UserID != 0 || save_size || save_visible || save_order || (save_sort && column->SortOrder != -1); + if (!save_column) + continue; buf->appendf("Column %-2d", column_n); if (column->UserID != 0) buf->appendf(" UserID=%08X", column->UserID); if (save_size && column->IsStretch) buf->appendf(" Weight=%.4f", column->WidthOrWeight); @@ -3341,10 +3467,9 @@ void ImGui::TableGcCompactTransientBuffers(ImGuiTable* table) //IMGUI_DEBUG_LOG("TableGcCompactTransientBuffers() id=0x%08X\n", table->ID); ImGuiContext& g = *GImGui; IM_ASSERT(table->MemoryCompacted == false); - table->DrawSplitter.ClearFreeMemory(); - table->SortSpecsMulti.clear(); table->SortSpecs.Specs = NULL; - table->IsSortSpecsDirty = true; + table->SortSpecsMulti.clear(); + table->IsSortSpecsDirty = true; // FIXME: shouldn't have to leak into user performing a sort table->ColumnsNames.clear(); table->MemoryCompacted = true; for (int n = 0; n < table->ColumnsCount; n++) @@ -3352,6 +3477,12 @@ void ImGui::TableGcCompactTransientBuffers(ImGuiTable* table) g.TablesLastTimeActive[g.Tables.GetIndex(table)] = -1.0f; } +void ImGui::TableGcCompactTransientBuffers(ImGuiTableTempData* temp_data) +{ + temp_data->DrawSplitter.ClearFreeMemory(); + temp_data->LastTimeActive = -1.0f; +} + // Compact and remove unused settings data (currently only used by TestEngine) void ImGui::TableGcCompactSettings() { @@ -3861,7 +3992,7 @@ void ImGui::EndColumns() const float column_hit_hw = COLUMNS_HIT_RECT_HALF_WIDTH; const ImRect column_hit_rect(ImVec2(x - column_hit_hw, y1), ImVec2(x + column_hit_hw, y2)); KeepAliveID(column_id); - if (IsClippedEx(column_hit_rect, column_id, false)) + if (IsClippedEx(column_hit_rect, column_id)) // FIXME: Can be removed or replaced with a lower-level test continue; bool hovered = false, held = false; diff --git a/apps/common/external/imgui/imgui_widgets.cpp b/apps/common/external/imgui/imgui_widgets.cpp index 931e8ec1c8..56723cdda5 100644 --- a/apps/common/external/imgui/imgui_widgets.cpp +++ b/apps/common/external/imgui/imgui_widgets.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.80 +// dear imgui, v1.87 // (widgets code) /* @@ -59,6 +59,8 @@ Index of this file: #if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later #pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types #endif +#pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2). +#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). #endif // Clang/GCC warnings with -Weverything @@ -122,7 +124,7 @@ static const ImU64 IM_U64_MAX = (2ULL * 9223372036854775807LL + 1); //------------------------------------------------------------------------- // For InputTextEx() -static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data); +static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data, ImGuiInputSource input_source); static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end); static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining = NULL, ImVec2* out_offset = NULL, bool stop_on_new_line = false); @@ -150,9 +152,13 @@ void ImGui::TextEx(const char* text, const char* text_end, ImGuiTextFlags flags) ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return; - ImGuiContext& g = *GImGui; - IM_ASSERT(text != NULL); + + // Accept null ranges + if (text == text_end) + text = text_end = ""; + + // Calculate length const char* text_begin = text; if (text_end == NULL) text_end = text + strlen(text); // FIXME-OPT @@ -199,7 +205,7 @@ void ImGui::TextEx(const char* text, const char* text_end, ImGuiTextFlags flags) ImRect line_rect(pos, pos + ImVec2(FLT_MAX, line_height)); while (line < text_end) { - if (IsClippedEx(line_rect, 0, false)) + if (IsClippedEx(line_rect, 0)) break; const char* line_end = (const char*)memchr(line, '\n', text_end - line); @@ -267,6 +273,7 @@ void ImGui::TextV(const char* fmt, va_list args) if (window->SkipItems) return; + // FIXME-OPT: Handle the %s shortcut? ImGuiContext& g = *GImGui; const char* text_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); TextEx(g.TempBuffer, text_end, ImGuiTextFlags_NoWidthForLargeClippedText); @@ -350,17 +357,20 @@ void ImGui::LabelTextV(const char* label, const char* fmt, va_list args) const ImGuiStyle& style = g.Style; const float w = CalcItemWidth(); + const char* value_text_begin = &g.TempBuffer[0]; + const char* value_text_end = value_text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); + const ImVec2 value_size = CalcTextSize(value_text_begin, value_text_end, false); const ImVec2 label_size = CalcTextSize(label, NULL, true); - const ImRect value_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2)); - const ImRect total_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w + (label_size.x > 0.0f ? style.ItemInnerSpacing.x : 0.0f), style.FramePadding.y * 2) + label_size); + + const ImVec2 pos = window->DC.CursorPos; + const ImRect value_bb(pos, pos + ImVec2(w, value_size.y + style.FramePadding.y * 2)); + const ImRect total_bb(pos, pos + ImVec2(w + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), ImMax(value_size.y, label_size.y) + style.FramePadding.y * 2)); ItemSize(total_bb, style.FramePadding.y); if (!ItemAdd(total_bb, 0)) return; // Render - const char* value_text_begin = &g.TempBuffer[0]; - const char* value_text_end = value_text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); - RenderTextClipped(value_bb.Min, value_bb.Max, value_text_begin, value_text_end, NULL, ImVec2(0.0f, 0.5f)); + RenderTextClipped(value_bb.Min + style.FramePadding, value_bb.Max, value_text_begin, value_text_end, &value_size, ImVec2(0.0f, 0.0f)); if (label_size.x > 0.0f) RenderText(ImVec2(value_bb.Max.x + style.ItemInnerSpacing.x, value_bb.Min.y + style.FramePadding.y), label); } @@ -483,14 +493,6 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool ImGuiContext& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); - if (flags & ImGuiButtonFlags_Disabled) - { - if (out_hovered) *out_hovered = false; - if (out_held) *out_held = false; - if (g.ActiveId == id) ClearActiveID(); - return false; - } - // Default only reacts to left mouse button if ((flags & ImGuiButtonFlags_MouseButtonMask_) == 0) flags |= ImGuiButtonFlags_MouseButtonDefault_; @@ -500,12 +502,12 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool flags |= ImGuiButtonFlags_PressedOnDefault_; ImGuiWindow* backup_hovered_window = g.HoveredWindow; - const bool flatten_hovered_children = (flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredRootWindow == window; + const bool flatten_hovered_children = (flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredWindow && g.HoveredWindow->RootWindow == window; if (flatten_hovered_children) g.HoveredWindow = window; #ifdef IMGUI_ENABLE_TEST_ENGINE - if (id != 0 && window->DC.LastItemId != id) + if (id != 0 && g.LastItemData.ID != id) IMGUI_TEST_ENGINE_ITEM_ADD(bb, id); #endif @@ -522,7 +524,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool { hovered = true; SetHoveredID(id); - if (CalcTypematicRepeatAmount(g.HoveredIdTimer + 0.0001f - g.IO.DeltaTime, g.HoveredIdTimer + 0.0001f, DRAGDROP_HOLD_TO_OPEN_TIMER, 0.00f)) + if (g.HoveredIdTimer - g.IO.DeltaTime <= DRAGDROP_HOLD_TO_OPEN_TIMER && g.HoveredIdTimer >= DRAGDROP_HOLD_TO_OPEN_TIMER) { pressed = true; g.DragDropHoldJustPressedId = id; @@ -562,13 +564,15 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool SetFocusID(id, window); FocusWindow(window); } - if ((flags & ImGuiButtonFlags_PressedOnClick) || ((flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseDoubleClicked[mouse_button_clicked])) + if ((flags & ImGuiButtonFlags_PressedOnClick) || ((flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseClickedCount[mouse_button_clicked] == 2)) { pressed = true; if (flags & ImGuiButtonFlags_NoHoldingActiveId) ClearActiveID(); else SetActiveID(id, window); // Hold on ID + if (!(flags & ImGuiButtonFlags_NoNavFocus)) + SetFocusID(id, window); g.ActiveIdMouseButton = mouse_button_clicked; FocusWindow(window); } @@ -579,6 +583,8 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool const bool has_repeated_at_least_once = (flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[mouse_button_released] >= g.IO.KeyRepeatDelay; if (!has_repeated_at_least_once) pressed = true; + if (!(flags & ImGuiButtonFlags_NoNavFocus)) + SetFocusID(id, window); ClearActiveID(); } @@ -603,13 +609,12 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool bool nav_activated_by_code = (g.NavActivateId == id); bool nav_activated_by_inputs = IsNavInputTest(ImGuiNavInput_Activate, (flags & ImGuiButtonFlags_Repeat) ? ImGuiInputReadMode_Repeat : ImGuiInputReadMode_Pressed); if (nav_activated_by_code || nav_activated_by_inputs) - pressed = true; - if (nav_activated_by_code || nav_activated_by_inputs || g.ActiveId == id) { // Set active id so it can be queried by user via IsItemActive(), equivalent of holding the mouse button. - g.NavActivateId = id; // This is so SetActiveId assign a Nav source + pressed = true; SetActiveID(id, window); - if ((nav_activated_by_code || nav_activated_by_inputs) && !(flags & ImGuiButtonFlags_NoNavFocus)) + g.ActiveIdSource = ImGuiInputSource_Nav; + if (!(flags & ImGuiButtonFlags_NoNavFocus)) SetFocusID(id, window); } } @@ -636,7 +641,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool if ((release_in || release_anywhere) && !g.DragDropActive) { // Report as pressed when releasing the mouse (this is the most common path) - bool is_double_click_release = (flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseDownWasDoubleClick[mouse_button]; + bool is_double_click_release = (flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseReleased[mouse_button] && g.IO.MouseClickedLastCount[mouse_button] == 2; bool is_repeating_already = (flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[mouse_button] >= g.IO.KeyRepeatDelay; // Repeat mode trumps if (!is_double_click_release && !is_repeating_already) pressed = true; @@ -683,8 +688,9 @@ bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags if (!ItemAdd(bb, id)) return false; - if (window->DC.ItemFlags & ImGuiItemFlags_ButtonRepeat) + if (g.LastItemData.InFlags & ImGuiItemFlags_ButtonRepeat) flags |= ImGuiButtonFlags_Repeat; + bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags); @@ -692,13 +698,16 @@ bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); RenderNavHighlight(bb, id); RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding); + + if (g.LogEnabled) + LogSetNextTextDecoration("[", "]"); RenderTextClipped(bb.Min + style.FramePadding, bb.Max - style.FramePadding, label, NULL, &label_size, style.ButtonTextAlign, &bb); // Automatically close popups //if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup)) // CloseCurrentPopup(); - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.LastItemStatusFlags); + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); return pressed; } @@ -722,6 +731,7 @@ bool ImGui::SmallButton(const char* label) // Then you can keep 'str_id' empty or the same for all your buttons (instead of creating a string based on a non-string id) bool ImGui::InvisibleButton(const char* str_id, const ImVec2& size_arg, ImGuiButtonFlags flags) { + ImGuiContext& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; @@ -739,16 +749,17 @@ bool ImGui::InvisibleButton(const char* str_id, const ImVec2& size_arg, ImGuiBut bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags); + IMGUI_TEST_ENGINE_ITEM_INFO(id, str_id, g.LastItemData.StatusFlags); return pressed; } bool ImGui::ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size, ImGuiButtonFlags flags) { + ImGuiContext& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; - ImGuiContext& g = *GImGui; const ImGuiID id = window->GetID(str_id); const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); const float default_size = GetFrameHeight(); @@ -756,7 +767,7 @@ bool ImGui::ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size, ImGuiBu if (!ItemAdd(bb, id)) return false; - if (window->DC.ItemFlags & ImGuiItemFlags_ButtonRepeat) + if (g.LastItemData.InFlags & ImGuiItemFlags_ButtonRepeat) flags |= ImGuiButtonFlags_Repeat; bool hovered, held; @@ -769,6 +780,7 @@ bool ImGui::ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size, ImGuiBu RenderFrame(bb.Min, bb.Max, bg_col, true, g.Style.FrameRounding); RenderArrow(window->DrawList, bb.Min + ImVec2(ImMax(0.0f, (size.x - g.FontSize) * 0.5f), ImMax(0.0f, (size.y - g.FontSize) * 0.5f)), text_col, dir); + IMGUI_TEST_ENGINE_ITEM_INFO(id, str_id, g.LastItemData.StatusFlags); return pressed; } @@ -779,22 +791,30 @@ bool ImGui::ArrowButton(const char* str_id, ImGuiDir dir) } // Button to close a window -bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos)//, float size) +bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - // We intentionally allow interaction when clipped so that a mechanical Alt,Right,Validate sequence close a window. - // (this isn't the regular behavior of buttons, but it doesn't affect the user much because navigation tends to keep items visible). + // Tweak 1: Shrink hit-testing area if button covers an abnormally large proportion of the visible region. That's in order to facilitate moving the window away. (#3825) + // This may better be applied as a general hit-rect reduction mechanism for all widgets to ensure the area to move window is always accessible? const ImRect bb(pos, pos + ImVec2(g.FontSize, g.FontSize) + g.Style.FramePadding * 2.0f); - bool is_clipped = !ItemAdd(bb, id); + ImRect bb_interact = bb; + const float area_to_visible_ratio = window->OuterRectClipped.GetArea() / bb.GetArea(); + if (area_to_visible_ratio < 1.5f) + bb_interact.Expand(ImFloor(bb_interact.GetSize() * -0.25f)); + + // Tweak 2: We intentionally allow interaction when clipped so that a mechanical Alt,Right,Activate sequence can always close a window. + // (this isn't the regular behavior of buttons, but it doesn't affect the user much because navigation tends to keep items visible). + bool is_clipped = !ItemAdd(bb_interact, id); bool hovered, held; - bool pressed = ButtonBehavior(bb, id, &hovered, &held); + bool pressed = ButtonBehavior(bb_interact, id, &hovered, &held); if (is_clipped) return pressed; // Render + // FIXME: Clarify this mess ImU32 col = GetColorU32(held ? ImGuiCol_ButtonActive : ImGuiCol_ButtonHovered); ImVec2 center = bb.GetCenter(); if (hovered) @@ -863,23 +883,25 @@ void ImGui::Scrollbar(ImGuiAxis axis) // Calculate scrollbar bounding box ImRect bb = GetWindowScrollbarRect(window, axis); - ImDrawCornerFlags rounding_corners = 0; + ImDrawFlags rounding_corners = ImDrawFlags_RoundCornersNone; if (axis == ImGuiAxis_X) { - rounding_corners |= ImDrawCornerFlags_BotLeft; + rounding_corners |= ImDrawFlags_RoundCornersBottomLeft; if (!window->ScrollbarY) - rounding_corners |= ImDrawCornerFlags_BotRight; + rounding_corners |= ImDrawFlags_RoundCornersBottomRight; } else { if ((window->Flags & ImGuiWindowFlags_NoTitleBar) && !(window->Flags & ImGuiWindowFlags_MenuBar)) - rounding_corners |= ImDrawCornerFlags_TopRight; + rounding_corners |= ImDrawFlags_RoundCornersTopRight; if (!window->ScrollbarX) - rounding_corners |= ImDrawCornerFlags_BotRight; + rounding_corners |= ImDrawFlags_RoundCornersBottomRight; } float size_avail = window->InnerRect.Max[axis] - window->InnerRect.Min[axis]; float size_contents = window->ContentSize[axis] + window->WindowPadding[axis] * 2.0f; - ScrollbarEx(bb, id, axis, &window->Scroll[axis], size_avail, size_contents, rounding_corners); + ImS64 scroll = (ImS64)window->Scroll[axis]; + ScrollbarEx(bb, id, axis, &scroll, (ImS64)size_avail, (ImS64)size_contents, rounding_corners); + window->Scroll[axis] = (float)scroll; } // Vertical/Horizontal scrollbar @@ -888,7 +910,7 @@ void ImGui::Scrollbar(ImGuiAxis axis) // - We store values as normalized ratio and in a form that allows the window content to change while we are holding on a scrollbar // - We handle both horizontal and vertical scrollbars, which makes the terminology not ideal. // Still, the code should probably be made simpler.. -bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, float* p_scroll_v, float size_avail_v, float size_contents_v, ImDrawCornerFlags rounding_corners) +bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, ImS64* p_scroll_v, ImS64 size_avail_v, ImS64 size_contents_v, ImDrawFlags flags) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; @@ -919,8 +941,8 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, floa // Calculate the height of our grabbable box. It generally represent the amount visible (vs the total scrollable amount) // But we maintain a minimum size in pixel to allow for the user to still aim inside. IM_ASSERT(ImMax(size_contents_v, size_avail_v) > 0.0f); // Adding this assert to check if the ImMax(XXX,1.0f) is still needed. PLEASE CONTACT ME if this triggers. - const float win_size_v = ImMax(ImMax(size_contents_v, size_avail_v), 1.0f); - const float grab_h_pixels = ImClamp(scrollbar_size_v * (size_avail_v / win_size_v), style.GrabMinSize, scrollbar_size_v); + const ImS64 win_size_v = ImMax(ImMax(size_contents_v, size_avail_v), (ImS64)1); + const float grab_h_pixels = ImClamp(scrollbar_size_v * ((float)size_avail_v / (float)win_size_v), style.GrabMinSize, scrollbar_size_v); const float grab_h_norm = grab_h_pixels / scrollbar_size_v; // Handle input right away. None of the code of Begin() is relying on scrolling position before calling Scrollbar(). @@ -928,13 +950,13 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, floa bool hovered = false; ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_NoNavFocus); - float scroll_max = ImMax(1.0f, size_contents_v - size_avail_v); - float scroll_ratio = ImSaturate(*p_scroll_v / scroll_max); + const ImS64 scroll_max = ImMax((ImS64)1, size_contents_v - size_avail_v); + float scroll_ratio = ImSaturate((float)*p_scroll_v / (float)scroll_max); float grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v; // Grab position in normalized space if (held && allow_interaction && grab_h_norm < 1.0f) { - float scrollbar_pos_v = bb.Min[axis]; - float mouse_pos_v = g.IO.MousePos[axis]; + const float scrollbar_pos_v = bb.Min[axis]; + const float mouse_pos_v = g.IO.MousePos[axis]; // Click position in scrollbar normalized space (0.0f->1.0f) const float clicked_v_norm = ImSaturate((mouse_pos_v - scrollbar_pos_v) / scrollbar_size_v); @@ -954,10 +976,10 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, floa // Apply scroll (p_scroll_v will generally point on one member of window->Scroll) // It is ok to modify Scroll here because we are being called in Begin() after the calculation of ContentSize and before setting up our starting position const float scroll_v_norm = ImSaturate((clicked_v_norm - g.ScrollbarClickDeltaToGrabCenter - grab_h_norm * 0.5f) / (1.0f - grab_h_norm)); - *p_scroll_v = IM_ROUND(scroll_v_norm * scroll_max);//(win_size_contents_v - win_size_v)); + *p_scroll_v = (ImS64)(scroll_v_norm * scroll_max); // Update values for rendering - scroll_ratio = ImSaturate(*p_scroll_v / scroll_max); + scroll_ratio = ImSaturate((float)*p_scroll_v / (float)scroll_max); grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v; // Update distance to grab now that we have seeked and saturated @@ -968,7 +990,7 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, floa // Render const ImU32 bg_col = GetColorU32(ImGuiCol_ScrollbarBg); const ImU32 grab_col = GetColorU32(held ? ImGuiCol_ScrollbarGrabActive : hovered ? ImGuiCol_ScrollbarGrabHovered : ImGuiCol_ScrollbarGrab, alpha); - window->DrawList->AddRectFilled(bb_frame.Min, bb_frame.Max, bg_col, window->WindowRounding, rounding_corners); + window->DrawList->AddRectFilled(bb_frame.Min, bb_frame.Max, bg_col, window->WindowRounding, flags); ImRect grab_rect; if (axis == ImGuiAxis_X) grab_rect = ImRect(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm), bb.Min.y, ImLerp(bb.Min.x, bb.Max.x, grab_v_norm) + grab_h_pixels, bb.Max.y); @@ -1067,7 +1089,7 @@ bool ImGui::Checkbox(const char* label, bool* v) ItemSize(total_bb, style.FramePadding.y); if (!ItemAdd(total_bb, id)) { - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0)); + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0)); return false; } @@ -1083,7 +1105,7 @@ bool ImGui::Checkbox(const char* label, bool* v) RenderNavHighlight(total_bb, id); RenderFrame(check_bb.Min, check_bb.Max, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding); ImU32 check_col = GetColorU32(ImGuiCol_CheckMark); - bool mixed_value = (window->DC.ItemFlags & ImGuiItemFlags_MixedValue) != 0; + bool mixed_value = (g.LastItemData.InFlags & ImGuiItemFlags_MixedValue) != 0; if (mixed_value) { // Undocumented tristate/mixed/indeterminate checkbox (#2644) @@ -1097,12 +1119,13 @@ bool ImGui::Checkbox(const char* label, bool* v) RenderCheckMark(window->DrawList, check_bb.Min + ImVec2(pad, pad), check_col, square_sz - pad * 2.0f); } + ImVec2 label_pos = ImVec2(check_bb.Max.x + style.ItemInnerSpacing.x, check_bb.Min.y + style.FramePadding.y); if (g.LogEnabled) - LogRenderedText(&total_bb.Min, mixed_value ? "[~]" : *v ? "[x]" : "[ ]"); + LogRenderedText(&label_pos, mixed_value ? "[~]" : *v ? "[x]" : "[ ]"); if (label_size.x > 0.0f) - RenderText(ImVec2(check_bb.Max.x + style.ItemInnerSpacing.x, check_bb.Min.y + style.FramePadding.y), label); + RenderText(label_pos, label); - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0)); + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0)); return pressed; } @@ -1114,11 +1137,11 @@ bool ImGui::CheckboxFlagsT(const char* label, T* flags, T flags_value) bool pressed; if (!all_on && any_on) { - ImGuiWindow* window = GetCurrentWindow(); - ImGuiItemFlags backup_item_flags = window->DC.ItemFlags; - window->DC.ItemFlags |= ImGuiItemFlags_MixedValue; + ImGuiContext& g = *GImGui; + ImGuiItemFlags backup_item_flags = g.CurrentItemFlags; + g.CurrentItemFlags |= ImGuiItemFlags_MixedValue; pressed = Checkbox(label, &all_on); - window->DC.ItemFlags = backup_item_flags; + g.CurrentItemFlags = backup_item_flags; } else { @@ -1198,12 +1221,13 @@ bool ImGui::RadioButton(const char* label, bool active) window->DrawList->AddCircle(center, radius, GetColorU32(ImGuiCol_Border), 16, style.FrameBorderSize); } + ImVec2 label_pos = ImVec2(check_bb.Max.x + style.ItemInnerSpacing.x, check_bb.Min.y + style.FramePadding.y); if (g.LogEnabled) - LogRenderedText(&total_bb.Min, active ? "(x)" : "( )"); + LogRenderedText(&label_pos, active ? "(x)" : "( )"); if (label_size.x > 0.0f) - RenderText(ImVec2(check_bb.Max.x + style.ItemInnerSpacing.x, check_bb.Min.y + style.FramePadding.y), label); + RenderText(label_pos, label); - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags); + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); return pressed; } @@ -1372,11 +1396,20 @@ void ImGui::SeparatorEx(ImGuiSeparatorFlags flags) if (g.GroupStack.Size > 0 && g.GroupStack.back().WindowID == window->ID) x1 += window->DC.Indent.x; + // FIXME-WORKRECT: In theory we should simply be using WorkRect.Min.x/Max.x everywhere but it isn't aesthetically what we want, + // need to introduce a variant of WorkRect for that purpose. (#4787) + if (ImGuiTable* table = g.CurrentTable) + { + x1 = table->Columns[table->CurrentColumn].MinX; + x2 = table->Columns[table->CurrentColumn].MaxX; + } + ImGuiOldColumns* columns = (flags & ImGuiSeparatorFlags_SpanAllColumns) ? window->DC.CurrentColumns : NULL; if (columns) PushColumnsBackground(); // We don't provide our width to the layout so that it doesn't get feed back into AutoFit + // FIXME: This prevents ->CursorMaxPos based bounding box evaluation from working (e.g. TableEndCell) const ImRect bb(ImVec2(x1, window->DC.CursorPos.y), ImVec2(x2, window->DC.CursorPos.y + thickness_draw)); ItemSize(ImVec2(0.0f, thickness_layout)); const bool item_visible = ItemAdd(bb, 0); @@ -1385,7 +1418,8 @@ void ImGui::SeparatorEx(ImGuiSeparatorFlags flags) // Draw window->DrawList->AddLine(bb.Min, ImVec2(bb.Max.x, bb.Min.y), GetColorU32(ImGuiCol_Separator)); if (g.LogEnabled) - LogRenderedText(&bb.Min, "--------------------------------"); + LogRenderedText(&bb.Min, "--------------------------------\n"); + } if (columns) { @@ -1404,7 +1438,7 @@ void ImGui::Separator() // Those flags should eventually be overridable by the user ImGuiSeparatorFlags flags = (window->DC.LayoutType == ImGuiLayoutType_Horizontal) ? ImGuiSeparatorFlags_Vertical : ImGuiSeparatorFlags_Horizontal; - flags |= ImGuiSeparatorFlags_SpanAllColumns; + flags |= ImGuiSeparatorFlags_SpanAllColumns; // NB: this only applies to legacy Columns() api as they relied on Separator() a lot. SeparatorEx(flags); } @@ -1414,10 +1448,10 @@ bool ImGui::SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags; - window->DC.ItemFlags |= ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus; + const ImGuiItemFlags item_flags_backup = g.CurrentItemFlags; + g.CurrentItemFlags |= ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus; bool item_add = ItemAdd(bb, id); - window->DC.ItemFlags = item_flags_backup; + g.CurrentItemFlags = item_flags_backup; if (!item_add) return false; @@ -1425,10 +1459,12 @@ bool ImGui::SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float ImRect bb_interact = bb; bb_interact.Expand(axis == ImGuiAxis_Y ? ImVec2(0.0f, hover_extend) : ImVec2(hover_extend, 0.0f)); ButtonBehavior(bb_interact, id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowItemOverlap); + if (hovered) + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredRect; // for IsItemHovered(), because bb_interact is larger than bb if (g.ActiveId != id) SetItemAllowOverlap(); - if (held || (g.HoveredId == id && g.HoveredIdPreviousFrame == id && g.HoveredIdTimer >= hover_visibility_delay)) + if (held || (hovered && g.HoveredIdPreviousFrame == id && g.HoveredIdTimer >= hover_visibility_delay)) SetMouseCursor(axis == ImGuiAxis_Y ? ImGuiMouseCursor_ResizeNS : ImGuiMouseCursor_ResizeEW); ImRect bb_render = bb; @@ -1518,8 +1554,12 @@ void ImGui::ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_exc //------------------------------------------------------------------------- // [SECTION] Widgets: ComboBox //------------------------------------------------------------------------- +// - CalcMaxPopupHeightFromItemCount() [Internal] // - BeginCombo() +// - BeginComboPopup() [Internal] // - EndCombo() +// - BeginComboPreview() [Internal] +// - EndComboPreview() [Internal] // - Combo() //------------------------------------------------------------------------- @@ -1533,74 +1573,99 @@ static float CalcMaxPopupHeightFromItemCount(int items_count) bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboFlags flags) { - // Always consume the SetNextWindowSizeConstraint() call in our early return paths ImGuiContext& g = *GImGui; - bool has_window_size_constraint = (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint) != 0; - g.NextWindowData.Flags &= ~ImGuiNextWindowDataFlags_HasSizeConstraint; - ImGuiWindow* window = GetCurrentWindow(); + + ImGuiNextWindowDataFlags backup_next_window_data_flags = g.NextWindowData.Flags; + g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values if (window->SkipItems) return false; - IM_ASSERT((flags & (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)) != (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)); // Can't use both flags together - const ImGuiStyle& style = g.Style; const ImGuiID id = window->GetID(label); + IM_ASSERT((flags & (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)) != (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)); // Can't use both flags together const float arrow_size = (flags & ImGuiComboFlags_NoArrowButton) ? 0.0f : GetFrameHeight(); const ImVec2 label_size = CalcTextSize(label, NULL, true); - const float expected_w = CalcItemWidth(); - const float w = (flags & ImGuiComboFlags_NoPreview) ? arrow_size : expected_w; - const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f)); - const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); + const float w = (flags & ImGuiComboFlags_NoPreview) ? arrow_size : CalcItemWidth(); + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f)); + const ImRect total_bb(bb.Min, bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); ItemSize(total_bb, style.FramePadding.y); - if (!ItemAdd(total_bb, id, &frame_bb)) + if (!ItemAdd(total_bb, id, &bb)) return false; + // Open on click bool hovered, held; - bool pressed = ButtonBehavior(frame_bb, id, &hovered, &held); - bool popup_open = IsPopupOpen(id, ImGuiPopupFlags_None); + bool pressed = ButtonBehavior(bb, id, &hovered, &held); + const ImGuiID popup_id = ImHashStr("##ComboPopup", 0, id); + bool popup_open = IsPopupOpen(popup_id, ImGuiPopupFlags_None); + if (pressed && !popup_open) + { + OpenPopupEx(popup_id, ImGuiPopupFlags_None); + popup_open = true; + } + // Render shape const ImU32 frame_col = GetColorU32(hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); - const float value_x2 = ImMax(frame_bb.Min.x, frame_bb.Max.x - arrow_size); - RenderNavHighlight(frame_bb, id); + const float value_x2 = ImMax(bb.Min.x, bb.Max.x - arrow_size); + RenderNavHighlight(bb, id); if (!(flags & ImGuiComboFlags_NoPreview)) - window->DrawList->AddRectFilled(frame_bb.Min, ImVec2(value_x2, frame_bb.Max.y), frame_col, style.FrameRounding, (flags & ImGuiComboFlags_NoArrowButton) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Left); + window->DrawList->AddRectFilled(bb.Min, ImVec2(value_x2, bb.Max.y), frame_col, style.FrameRounding, (flags & ImGuiComboFlags_NoArrowButton) ? ImDrawFlags_RoundCornersAll : ImDrawFlags_RoundCornersLeft); if (!(flags & ImGuiComboFlags_NoArrowButton)) { ImU32 bg_col = GetColorU32((popup_open || hovered) ? ImGuiCol_ButtonHovered : ImGuiCol_Button); ImU32 text_col = GetColorU32(ImGuiCol_Text); - window->DrawList->AddRectFilled(ImVec2(value_x2, frame_bb.Min.y), frame_bb.Max, bg_col, style.FrameRounding, (w <= arrow_size) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Right); - if (value_x2 + arrow_size - style.FramePadding.x <= frame_bb.Max.x) - RenderArrow(window->DrawList, ImVec2(value_x2 + style.FramePadding.y, frame_bb.Min.y + style.FramePadding.y), text_col, ImGuiDir_Down, 1.0f); + window->DrawList->AddRectFilled(ImVec2(value_x2, bb.Min.y), bb.Max, bg_col, style.FrameRounding, (w <= arrow_size) ? ImDrawFlags_RoundCornersAll : ImDrawFlags_RoundCornersRight); + if (value_x2 + arrow_size - style.FramePadding.x <= bb.Max.x) + RenderArrow(window->DrawList, ImVec2(value_x2 + style.FramePadding.y, bb.Min.y + style.FramePadding.y), text_col, ImGuiDir_Down, 1.0f); } - RenderFrameBorder(frame_bb.Min, frame_bb.Max, style.FrameRounding); - if (preview_value != NULL && !(flags & ImGuiComboFlags_NoPreview)) - RenderTextClipped(frame_bb.Min + style.FramePadding, ImVec2(value_x2, frame_bb.Max.y), preview_value, NULL, NULL, ImVec2(0.0f, 0.0f)); - if (label_size.x > 0) - RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); + RenderFrameBorder(bb.Min, bb.Max, style.FrameRounding); - if ((pressed || g.NavActivateId == id) && !popup_open) + // Custom preview + if (flags & ImGuiComboFlags_CustomPreview) { - if (window->DC.NavLayerCurrent == 0) - window->NavLastIds[0] = id; - OpenPopupEx(id, ImGuiPopupFlags_None); - popup_open = true; + g.ComboPreviewData.PreviewRect = ImRect(bb.Min.x, bb.Min.y, value_x2, bb.Max.y); + IM_ASSERT(preview_value == NULL || preview_value[0] == 0); + preview_value = NULL; + } + + // Render preview and label + if (preview_value != NULL && !(flags & ImGuiComboFlags_NoPreview)) + { + if (g.LogEnabled) + LogSetNextTextDecoration("{", "}"); + RenderTextClipped(bb.Min + style.FramePadding, ImVec2(value_x2, bb.Max.y), preview_value, NULL, NULL); } + if (label_size.x > 0) + RenderText(ImVec2(bb.Max.x + style.ItemInnerSpacing.x, bb.Min.y + style.FramePadding.y), label); if (!popup_open) return false; - if (has_window_size_constraint) + g.NextWindowData.Flags = backup_next_window_data_flags; + return BeginComboPopup(popup_id, bb, flags); +} + +bool ImGui::BeginComboPopup(ImGuiID popup_id, const ImRect& bb, ImGuiComboFlags flags) +{ + ImGuiContext& g = *GImGui; + if (!IsPopupOpen(popup_id, ImGuiPopupFlags_None)) + { + g.NextWindowData.ClearFlags(); + return false; + } + + // Set popup size + float w = bb.GetWidth(); + if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint) { - g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSizeConstraint; g.NextWindowData.SizeConstraintRect.Min.x = ImMax(g.NextWindowData.SizeConstraintRect.Min.x, w); } else { if ((flags & ImGuiComboFlags_HeightMask_) == 0) flags |= ImGuiComboFlags_HeightRegular; - IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiComboFlags_HeightMask_)); // Only one + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiComboFlags_HeightMask_)); // Only one int popup_max_height_in_items = -1; if (flags & ImGuiComboFlags_HeightRegular) popup_max_height_in_items = 8; else if (flags & ImGuiComboFlags_HeightSmall) popup_max_height_in_items = 4; @@ -1608,30 +1673,27 @@ bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboF SetNextWindowSizeConstraints(ImVec2(w, 0.0f), ImVec2(FLT_MAX, CalcMaxPopupHeightFromItemCount(popup_max_height_in_items))); } + // This is essentially a specialized version of BeginPopupEx() char name[16]; ImFormatString(name, IM_ARRAYSIZE(name), "##Combo_%02d", g.BeginPopupStack.Size); // Recycle windows based on depth - // Position the window given a custom constraint (peak into expected window size so we can position it) - // This might be easier to express with an hypothetical SetNextWindowPosConstraints() function. + // Set position given a custom constraint (peak into expected window size so we can position it) + // FIXME: This might be easier to express with an hypothetical SetNextWindowPosConstraints() function? + // FIXME: This might be moved to Begin() or at least around the same spot where Tooltips and other Popups are calling FindBestWindowPosForPopupEx()? if (ImGuiWindow* popup_window = FindWindowByName(name)) if (popup_window->WasActive) { // Always override 'AutoPosLastDirection' to not leave a chance for a past value to affect us. ImVec2 size_expected = CalcWindowNextAutoFitSize(popup_window); - if (flags & ImGuiComboFlags_PopupAlignLeft) - popup_window->AutoPosLastDirection = ImGuiDir_Left; // "Below, Toward Left" - else - popup_window->AutoPosLastDirection = ImGuiDir_Down; // "Below, Toward Right (default)" - ImRect r_outer = GetWindowAllowedExtentRect(popup_window); - ImVec2 pos = FindBestWindowPosForPopupEx(frame_bb.GetBL(), size_expected, &popup_window->AutoPosLastDirection, r_outer, frame_bb, ImGuiPopupPositionPolicy_ComboBox); + popup_window->AutoPosLastDirection = (flags & ImGuiComboFlags_PopupAlignLeft) ? ImGuiDir_Left : ImGuiDir_Down; // Left = "Below, Toward Left", Down = "Below, Toward Right (default)" + ImRect r_outer = GetPopupAllowedExtentRect(popup_window); + ImVec2 pos = FindBestWindowPosForPopupEx(bb.GetBL(), size_expected, &popup_window->AutoPosLastDirection, r_outer, bb, ImGuiPopupPositionPolicy_ComboBox); SetNextWindowPos(pos); } // We don't use BeginPopupEx() solely because we have a custom name string, which we could make an argument to BeginPopupEx() ImGuiWindowFlags window_flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_Popup | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoMove; - - // Horizontally align ourselves with the framed text - PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(style.FramePadding.x, style.WindowPadding.y)); + PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(g.Style.FramePadding.x, g.Style.WindowPadding.y)); // Horizontally align ourselves with the framed text bool ret = Begin(name, NULL, window_flags); PopStyleVar(); if (!ret) @@ -1648,6 +1710,57 @@ void ImGui::EndCombo() EndPopup(); } +// Call directly after the BeginCombo/EndCombo block. The preview is designed to only host non-interactive elements +// (Experimental, see GitHub issues: #1658, #4168) +bool ImGui::BeginComboPreview() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + ImGuiComboPreviewData* preview_data = &g.ComboPreviewData; + + if (window->SkipItems || !window->ClipRect.Overlaps(g.LastItemData.Rect)) // FIXME: Because we don't have a ImGuiItemStatusFlags_Visible flag to test last ItemAdd() result + return false; + IM_ASSERT(g.LastItemData.Rect.Min.x == preview_data->PreviewRect.Min.x && g.LastItemData.Rect.Min.y == preview_data->PreviewRect.Min.y); // Didn't call after BeginCombo/EndCombo block or forgot to pass ImGuiComboFlags_CustomPreview flag? + if (!window->ClipRect.Contains(preview_data->PreviewRect)) // Narrower test (optional) + return false; + + // FIXME: This could be contained in a PushWorkRect() api + preview_data->BackupCursorPos = window->DC.CursorPos; + preview_data->BackupCursorMaxPos = window->DC.CursorMaxPos; + preview_data->BackupCursorPosPrevLine = window->DC.CursorPosPrevLine; + preview_data->BackupPrevLineTextBaseOffset = window->DC.PrevLineTextBaseOffset; + preview_data->BackupLayout = window->DC.LayoutType; + window->DC.CursorPos = preview_data->PreviewRect.Min + g.Style.FramePadding; + window->DC.CursorMaxPos = window->DC.CursorPos; + window->DC.LayoutType = ImGuiLayoutType_Horizontal; + PushClipRect(preview_data->PreviewRect.Min, preview_data->PreviewRect.Max, true); + + return true; +} + +void ImGui::EndComboPreview() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + ImGuiComboPreviewData* preview_data = &g.ComboPreviewData; + + // FIXME: Using CursorMaxPos approximation instead of correct AABB which we will store in ImDrawCmd in the future + ImDrawList* draw_list = window->DrawList; + if (window->DC.CursorMaxPos.x < preview_data->PreviewRect.Max.x && window->DC.CursorMaxPos.y < preview_data->PreviewRect.Max.y) + if (draw_list->CmdBuffer.Size > 1) // Unlikely case that the PushClipRect() didn't create a command + { + draw_list->_CmdHeader.ClipRect = draw_list->CmdBuffer[draw_list->CmdBuffer.Size - 1].ClipRect = draw_list->CmdBuffer[draw_list->CmdBuffer.Size - 2].ClipRect; + draw_list->_TryMergeDrawCmds(); + } + PopClipRect(); + window->DC.CursorPos = preview_data->BackupCursorPos; + window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, preview_data->BackupCursorMaxPos); + window->DC.CursorPosPrevLine = preview_data->BackupCursorPosPrevLine; + window->DC.PrevLineTextBaseOffset = preview_data->BackupPrevLineTextBaseOffset; + window->DC.LayoutType = preview_data->BackupLayout; + preview_data->PreviewRect = ImRect(); +} + // Getter for the old Combo() API: const char*[] static bool Items_ArrayGetter(void* data, int idx, const char** out_text) { @@ -1700,7 +1813,7 @@ bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(voi bool value_changed = false; for (int i = 0; i < items_count; i++) { - PushID((void*)(intptr_t)i); + PushID(i); const bool item_selected = (i == *current_item); const char* item_text; if (!items_getter(data, i, &item_text)) @@ -1716,8 +1829,9 @@ bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(voi } EndCombo(); + if (value_changed) - MarkItemEdited(g.CurrentWindow->DC.LastItemId); + MarkItemEdited(g.LastItemData.ID); return value_changed; } @@ -1771,7 +1885,7 @@ static const ImGuiDataTypeInfo GDataTypeInfo[] = { sizeof(ImS64), "S64", "%lld", "%lld" }, // ImGuiDataType_S64 { sizeof(ImU64), "U64", "%llu", "%llu" }, #endif - { sizeof(float), "float", "%f", "%f" }, // ImGuiDataType_Float (float are promoted to double in va_arg) + { sizeof(float), "float", "%.3f","%f" }, // ImGuiDataType_Float (float are promoted to double in va_arg) { sizeof(double), "double","%f", "%lf" }, // ImGuiDataType_Double }; IM_STATIC_ASSERT(IM_ARRAYSIZE(GDataTypeInfo) == ImGuiDataType_COUNT); @@ -1881,24 +1995,10 @@ void ImGui::DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, const // User can input math operators (e.g. +100) to edit a numerical values. // NB: This is _not_ a full expression evaluator. We should probably add one and replace this dumb mess.. -bool ImGui::DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* p_data, const char* format) +bool ImGui::DataTypeApplyFromText(const char* buf, ImGuiDataType data_type, void* p_data, const char* format) { while (ImCharIsBlankA(*buf)) buf++; - - // We don't support '-' op because it would conflict with inputing negative value. - // Instead you can use +-100 to subtract from an existing value - char op = buf[0]; - if (op == '+' || op == '*' || op == '/') - { - buf++; - while (ImCharIsBlankA(*buf)) - buf++; - } - else - { - op = 0; - } if (!buf[0]) return false; @@ -1910,61 +2010,20 @@ bool ImGui::DataTypeApplyOpFromText(const char* buf, const char* initial_value_b if (format == NULL) format = type_info->ScanFmt; - // FIXME-LEGACY: The aim is to remove those operators and write a proper expression evaluator at some point.. - int arg1i = 0; - if (data_type == ImGuiDataType_S32) + if (data_type == ImGuiDataType_S32 || data_type == ImGuiDataType_U32 || data_type == ImGuiDataType_S64 || data_type == ImGuiDataType_U64 || data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double) { - int* v = (int*)p_data; - int arg0i = *v; - float arg1f = 0.0f; - if (op && sscanf(initial_value_buf, format, &arg0i) < 1) - return false; - // Store operand in a float so we can use fractional value for multipliers (*1.1), but constant always parsed as integer so we can fit big integers (e.g. 2000000003) past float precision - if (op == '+') { if (sscanf(buf, "%d", &arg1i)) *v = (int)(arg0i + arg1i); } // Add (use "+-" to subtract) - else if (op == '*') { if (sscanf(buf, "%f", &arg1f)) *v = (int)(arg0i * arg1f); } // Multiply - else if (op == '/') { if (sscanf(buf, "%f", &arg1f) && arg1f != 0.0f) *v = (int)(arg0i / arg1f); } // Divide - else { if (sscanf(buf, format, &arg1i) == 1) *v = arg1i; } // Assign constant - } - else if (data_type == ImGuiDataType_Float) - { - // For floats we have to ignore format with precision (e.g. "%.2f") because sscanf doesn't take them in - format = "%f"; - float* v = (float*)p_data; - float arg0f = *v, arg1f = 0.0f; - if (op && sscanf(initial_value_buf, format, &arg0f) < 1) - return false; - if (sscanf(buf, format, &arg1f) < 1) + // For float/double we have to ignore format with precision (e.g. "%.2f") because sscanf doesn't take them in, so force them into %f and %lf + if (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double) + format = type_info->ScanFmt; + if (sscanf(buf, format, p_data) < 1) return false; - if (op == '+') { *v = arg0f + arg1f; } // Add (use "+-" to subtract) - else if (op == '*') { *v = arg0f * arg1f; } // Multiply - else if (op == '/') { if (arg1f != 0.0f) *v = arg0f / arg1f; } // Divide - else { *v = arg1f; } // Assign constant - } - else if (data_type == ImGuiDataType_Double) - { - format = "%lf"; // scanf differentiate float/double unlike printf which forces everything to double because of ellipsis - double* v = (double*)p_data; - double arg0f = *v, arg1f = 0.0; - if (op && sscanf(initial_value_buf, format, &arg0f) < 1) - return false; - if (sscanf(buf, format, &arg1f) < 1) - return false; - if (op == '+') { *v = arg0f + arg1f; } // Add (use "+-" to subtract) - else if (op == '*') { *v = arg0f * arg1f; } // Multiply - else if (op == '/') { if (arg1f != 0.0f) *v = arg0f / arg1f; } // Divide - else { *v = arg1f; } // Assign constant - } - else if (data_type == ImGuiDataType_U32 || data_type == ImGuiDataType_S64 || data_type == ImGuiDataType_U64) - { - // All other types assign constant - // We don't bother handling support for legacy operators since they are a little too crappy. Instead we will later implement a proper expression evaluator in the future. - sscanf(buf, format, p_data); } else { // Small types need a 32-bit buffer to receive the result from scanf() int v32; - sscanf(buf, format, &v32); + if (sscanf(buf, format, &v32) < 1) + return false; if (data_type == ImGuiDataType_S8) *(ImS8*)p_data = (ImS8)ImClamp(v32, (int)IM_S8_MIN, (int)IM_S8_MAX); else if (data_type == ImGuiDataType_U8) @@ -2058,12 +2117,36 @@ static const char* ImAtoi(const char* src, TYPE* output) return src; } +// Sanitize format +// - Zero terminate so extra characters after format (e.g. "%f123") don't confuse atof/atoi +// - stb_sprintf.h supports several new modifiers which format numbers in a way that also makes them incompatible atof/atoi. +static void SanitizeFormatString(const char* fmt, char* fmt_out, size_t fmt_out_size) +{ + IM_UNUSED(fmt_out_size); + const char* fmt_end = ImParseFormatFindEnd(fmt); + IM_ASSERT((size_t)(fmt_end - fmt + 1) < fmt_out_size); // Format is too long, let us know if this happens to you! + while (fmt < fmt_end) + { + char c = *(fmt++); + if (c != '\'' && c != '$' && c != '_') // Custom flags provided by stb_sprintf.h. POSIX 2008 also supports '. + *(fmt_out++) = c; + } + *fmt_out = 0; // Zero-terminate +} + template TYPE ImGui::RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, TYPE v) { const char* fmt_start = ImParseFormatFindStart(format); if (fmt_start[0] != '%' || fmt_start[1] == '%') // Don't apply if the value is not visible in the format string return v; + + // Sanitize format + char fmt_sanitized[32]; + SanitizeFormatString(fmt_start, fmt_sanitized, IM_ARRAYSIZE(fmt_sanitized)); + fmt_start = fmt_sanitized; + + // Format value with our rounding, and read back char v_str[64]; ImFormatString(v_str, IM_ARRAYSIZE(v_str), fmt_start, v); const char* p = v_str; @@ -2101,9 +2184,9 @@ bool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const { ImGuiContext& g = *GImGui; const ImGuiAxis axis = (flags & ImGuiSliderFlags_Vertical) ? ImGuiAxis_Y : ImGuiAxis_X; - const bool is_decimal = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double); const bool is_clamped = (v_min < v_max); - const bool is_logarithmic = (flags & ImGuiSliderFlags_Logarithmic) && is_decimal; + const bool is_logarithmic = (flags & ImGuiSliderFlags_Logarithmic) != 0; + const bool is_floating_point = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double); // Default tweak speed if (v_speed == 0.0f && is_clamped && (v_max - v_min < FLT_MAX)) @@ -2121,7 +2204,7 @@ bool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const } else if (g.ActiveIdSource == ImGuiInputSource_Nav) { - int decimal_precision = is_decimal ? ImParseFormatPrecision(format, 3) : 0; + const int decimal_precision = is_floating_point ? ImParseFormatPrecision(format, 3) : 0; adjust_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard | ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_RepeatFast, 1.0f / 10.0f, 10.0f)[axis]; v_speed = ImMax(v_speed, GetMinimumStepAtDecimalPrecision(decimal_precision)); } @@ -2161,7 +2244,7 @@ bool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const if (is_logarithmic) { // When using logarithmic sliders, we need to clamp to avoid hitting zero, but our choice of clamp value greatly affects slider precision. We attempt to use the specified precision to estimate a good lower bound. - const int decimal_precision = is_decimal ? ImParseFormatPrecision(format, 3) : 1; + const int decimal_precision = is_floating_point ? ImParseFormatPrecision(format, 3) : 1; logarithmic_zero_epsilon = ImPow(0.1f, (float)decimal_precision); // Convert to parametric space, apply delta, convert back @@ -2199,9 +2282,9 @@ bool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const // Clamp values (+ handle overflow/wrap-around for integer types) if (*v != v_cur && is_clamped) { - if (v_cur < v_min || (v_cur > *v && adjust_delta < 0.0f && !is_decimal)) + if (v_cur < v_min || (v_cur > *v && adjust_delta < 0.0f && !is_floating_point)) v_cur = v_min; - if (v_cur > v_max || (v_cur < *v && adjust_delta > 0.0f && !is_decimal)) + if (v_cur > v_max || (v_cur < *v && adjust_delta > 0.0f && !is_floating_point)) v_cur = v_max; } @@ -2227,7 +2310,7 @@ bool ImGui::DragBehavior(ImGuiID id, ImGuiDataType data_type, void* p_v, float v } if (g.ActiveId != id) return false; - if ((g.CurrentWindow->DC.ItemFlags & ImGuiItemFlags_ReadOnly) || (flags & ImGuiSliderFlags_ReadOnly)) + if ((g.LastItemData.InFlags & ImGuiItemFlags_ReadOnly) || (flags & ImGuiSliderFlags_ReadOnly)) return false; switch (data_type) @@ -2260,12 +2343,14 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, const ImGuiStyle& style = g.Style; const ImGuiID id = window->GetID(label); const float w = CalcItemWidth(); + const ImVec2 label_size = CalcTextSize(label, NULL, true); const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f)); const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); + const bool temp_input_allowed = (flags & ImGuiSliderFlags_NoInput) == 0; ItemSize(total_bb, style.FramePadding.y); - if (!ItemAdd(total_bb, id, &frame_bb)) + if (!ItemAdd(total_bb, id, &frame_bb, temp_input_allowed ? ImGuiItemFlags_Inputable : 0)) return false; // Default format string when passing NULL @@ -2276,33 +2361,30 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, // Tabbing or CTRL-clicking on Drag turns it into an InputText const bool hovered = ItemHoverable(frame_bb, id); - const bool temp_input_allowed = (flags & ImGuiSliderFlags_NoInput) == 0; bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id); if (!temp_input_is_active) { - const bool focus_requested = temp_input_allowed && FocusableItemRegister(window, id); + const bool input_requested_by_tabbing = temp_input_allowed && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_FocusedByTabbing) != 0; const bool clicked = (hovered && g.IO.MouseClicked[0]); - const bool double_clicked = (hovered && g.IO.MouseDoubleClicked[0]); - if (focus_requested || clicked || double_clicked || g.NavActivateId == id || g.NavInputId == id) + const bool double_clicked = (hovered && g.IO.MouseClickedCount[0] == 2); + if (input_requested_by_tabbing || clicked || double_clicked || g.NavActivateId == id || g.NavActivateInputId == id) { SetActiveID(id, window); SetFocusID(id, window); FocusWindow(window); g.ActiveIdUsingNavDirMask = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right); - if (temp_input_allowed && (focus_requested || (clicked && g.IO.KeyCtrl) || double_clicked || g.NavInputId == id)) - { - temp_input_is_active = true; - FocusableItemUnregister(window); - } + if (temp_input_allowed) + if (input_requested_by_tabbing || (clicked && g.IO.KeyCtrl) || double_clicked || g.NavActivateInputId == id) + temp_input_is_active = true; } + // Experimental: simple click (without moving) turns Drag into an InputText - // FIXME: Currently polling ImGuiConfigFlags_IsTouchScreen, may either poll an hypothetical ImGuiBackendFlags_HasKeyboard and/or an explicit drag settings. if (g.IO.ConfigDragClickToInputText && temp_input_allowed && !temp_input_is_active) if (g.ActiveId == id && hovered && g.IO.MouseReleased[0] && !IsMouseDragPastThreshold(0, g.IO.MouseDragThreshold * DRAG_MOUSE_THRESHOLD_FACTOR)) { - g.NavInputId = id; + g.NavActivateId = g.NavActivateInputId = id; + g.NavActivateFlags = ImGuiActivateFlags_PreferInput; temp_input_is_active = true; - FocusableItemUnregister(window); } } @@ -2314,7 +2396,7 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, } // Draw frame - const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); + const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); RenderNavHighlight(frame_bb, id); RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, style.FrameRounding); @@ -2326,12 +2408,14 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. char value_buf[64]; const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, p_data, format); + if (g.LogEnabled) + LogSetNextTextDecoration("{", "}"); RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.5f)); if (label_size.x > 0.0f) RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags); + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); return value_changed; } @@ -2419,6 +2503,7 @@ bool ImGui::DragFloatRange2(const char* label, float* v_current_min, float* v_cu TextEx(label, FindRenderedTextEnd(label)); EndGroup(); PopID(); + return value_changed; } @@ -2589,8 +2674,8 @@ template TYPE ImGui::ScaleValueFromRatioT(ImGuiDataType data_type, float t, TYPE v_min, TYPE v_max, bool is_logarithmic, float logarithmic_zero_epsilon, float zero_deadzone_halfsize) { if (v_min == v_max) - return (TYPE)0.0f; - const bool is_decimal = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double); + return v_min; + const bool is_floating_point = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double); TYPE result; if (is_logarithmic) @@ -2638,7 +2723,7 @@ TYPE ImGui::ScaleValueFromRatioT(ImGuiDataType data_type, float t, TYPE v_min, T else { // Linear slider - if (is_decimal) + if (is_floating_point) { result = ImLerp(v_min, v_max, t); } @@ -2671,14 +2756,14 @@ bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_typ const ImGuiStyle& style = g.Style; const ImGuiAxis axis = (flags & ImGuiSliderFlags_Vertical) ? ImGuiAxis_Y : ImGuiAxis_X; - const bool is_decimal = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double); - const bool is_logarithmic = (flags & ImGuiSliderFlags_Logarithmic) && is_decimal; + const bool is_logarithmic = (flags & ImGuiSliderFlags_Logarithmic) != 0; + const bool is_floating_point = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double); const float grab_padding = 2.0f; const float slider_sz = (bb.Max[axis] - bb.Min[axis]) - grab_padding * 2.0f; float grab_sz = style.GrabMinSize; SIGNEDTYPE v_range = (v_min < v_max ? v_max - v_min : v_min - v_max); - if (!is_decimal && v_range >= 0) // v_range < 0 may happen on integer overflows + if (!is_floating_point && v_range >= 0) // v_range < 0 may happen on integer overflows grab_sz = ImMax((float)(slider_sz / (v_range + 1)), style.GrabMinSize); // For integer sliders: if possible have the grab size represent 1 unit grab_sz = ImMin(grab_sz, slider_sz); const float slider_usable_sz = slider_sz - grab_sz; @@ -2690,7 +2775,7 @@ bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_typ if (is_logarithmic) { // When using logarithmic sliders, we need to clamp to avoid hitting zero, but our choice of clamp value greatly affects slider precision. We attempt to use the specified precision to estimate a good lower bound. - const int decimal_precision = is_decimal ? ImParseFormatPrecision(format, 3) : 1; + const int decimal_precision = is_floating_point ? ImParseFormatPrecision(format, 3) : 1; logarithmic_zero_epsilon = ImPow(0.1f, (float)decimal_precision); zero_deadzone_halfsize = (style.LogSliderDeadzone * 0.5f) / ImMax(slider_usable_sz, 1.0f); } @@ -2728,7 +2813,7 @@ bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_typ float input_delta = (axis == ImGuiAxis_X) ? input_delta2.x : -input_delta2.y; if (input_delta != 0.0f) { - const int decimal_precision = is_decimal ? ImParseFormatPrecision(format, 3) : 0; + const int decimal_precision = is_floating_point ? ImParseFormatPrecision(format, 3) : 0; if (decimal_precision > 0) { input_delta /= 100.0f; // Gamepad/keyboard tweak speeds in % of slider bounds @@ -2831,7 +2916,7 @@ bool ImGui::SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type IM_ASSERT((flags == 1 || (flags & ImGuiSliderFlags_InvalidMask_) == 0) && "Invalid ImGuiSliderFlags flag! Has the 'float power' argument been mistakenly cast to flags? Call function with ImGuiSliderFlags_Logarithmic flags instead."); ImGuiContext& g = *GImGui; - if ((g.CurrentWindow->DC.ItemFlags & ImGuiItemFlags_ReadOnly) || (flags & ImGuiSliderFlags_ReadOnly)) + if ((g.LastItemData.InFlags & ImGuiItemFlags_ReadOnly) || (flags & ImGuiSliderFlags_ReadOnly)) return false; switch (data_type) @@ -2881,8 +2966,9 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f)); const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); + const bool temp_input_allowed = (flags & ImGuiSliderFlags_NoInput) == 0; ItemSize(total_bb, style.FramePadding.y); - if (!ItemAdd(total_bb, id, &frame_bb)) + if (!ItemAdd(total_bb, id, &frame_bb, temp_input_allowed ? ImGuiItemFlags_Inputable : 0)) return false; // Default format string when passing NULL @@ -2893,23 +2979,19 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat // Tabbing or CTRL-clicking on Slider turns it into an input box const bool hovered = ItemHoverable(frame_bb, id); - const bool temp_input_allowed = (flags & ImGuiSliderFlags_NoInput) == 0; bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id); if (!temp_input_is_active) { - const bool focus_requested = temp_input_allowed && FocusableItemRegister(window, id); + const bool input_requested_by_tabbing = temp_input_allowed && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_FocusedByTabbing) != 0; const bool clicked = (hovered && g.IO.MouseClicked[0]); - if (focus_requested || clicked || g.NavActivateId == id || g.NavInputId == id) + if (input_requested_by_tabbing || clicked || g.NavActivateId == id || g.NavActivateInputId == id) { SetActiveID(id, window); SetFocusID(id, window); FocusWindow(window); g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right); - if (temp_input_allowed && (focus_requested || (clicked && g.IO.KeyCtrl) || g.NavInputId == id)) - { + if (temp_input_allowed && (input_requested_by_tabbing || (clicked && g.IO.KeyCtrl) || g.NavActivateInputId == id)) temp_input_is_active = true; - FocusableItemUnregister(window); - } } } @@ -2921,7 +3003,7 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat } // Draw frame - const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); + const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); RenderNavHighlight(frame_bb, id); RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, g.Style.FrameRounding); @@ -2938,12 +3020,14 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. char value_buf[64]; const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, p_data, format); + if (g.LogEnabled) + LogSetNextTextDecoration("{", "}"); RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.5f)); if (label_size.x > 0.0f) RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags); + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); return value_changed; } @@ -3058,7 +3142,7 @@ bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType d format = PatchFormatStringFloatToInt(format); const bool hovered = ItemHoverable(frame_bb, id); - if ((hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || g.NavInputId == id) + if ((hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || g.NavActivateInputId == id) { SetActiveID(id, window); SetFocusID(id, window); @@ -3067,7 +3151,7 @@ bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType d } // Draw frame - const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); + const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); RenderNavHighlight(frame_bb, id); RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, g.Style.FrameRounding); @@ -3234,7 +3318,7 @@ bool ImGui::TempInputText(const ImRect& bb, ImGuiID id, const char* label, char* ClearActiveID(); g.CurrentWindow->DC.CursorPos = bb.Min; - bool value_changed = InputTextEx(label, NULL, buf, buf_size, bb.GetSize(), flags); + bool value_changed = InputTextEx(label, NULL, buf, buf_size, bb.GetSize(), flags | ImGuiInputTextFlags_MergedItem); if (init) { // First frame we started displaying the InputText widget, we expect it to take the active id. @@ -3249,8 +3333,6 @@ bool ImGui::TempInputText(const ImRect& bb, ImGuiID id, const char* label, char* // However this may not be ideal for all uses, as some user code may break on out of bound values. bool ImGui::TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* p_data, const char* format, const void* p_clamp_min, const void* p_clamp_max) { - ImGuiContext& g = *GImGui; - char fmt_buf[32]; char data_buf[32]; format = ImParseFormatTrimDecorations(format, fmt_buf, IM_ARRAYSIZE(fmt_buf)); @@ -3268,10 +3350,10 @@ bool ImGui::TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImG memcpy(&data_backup, p_data, data_type_size); // Apply new value (or operations) then clamp - DataTypeApplyOpFromText(data_buf, g.InputTextState.InitialTextA.Data, data_type, p_data, NULL); + DataTypeApplyFromText(data_buf, data_type, p_data, NULL); if (p_clamp_min || p_clamp_max) { - if (DataTypeCompare(data_type, p_clamp_min, p_clamp_max) > 0) + if (p_clamp_min && p_clamp_max && DataTypeCompare(data_type, p_clamp_min, p_clamp_max) > 0) ImSwap(p_clamp_min, p_clamp_max); DataTypeClamp(data_type, p_data, p_clamp_min, p_clamp_max); } @@ -3315,14 +3397,14 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data PushID(label); SetNextItemWidth(ImMax(1.0f, CalcItemWidth() - (button_size + style.ItemInnerSpacing.x) * 2)); if (InputText("", buf, IM_ARRAYSIZE(buf), flags)) // PushId(label) + "" gives us the expected ID from outside point of view - value_changed = DataTypeApplyOpFromText(buf, g.InputTextState.InitialTextA.Data, data_type, p_data, format); + value_changed = DataTypeApplyFromText(buf, data_type, p_data, format); // Step buttons const ImVec2 backup_frame_padding = style.FramePadding; style.FramePadding.x = style.FramePadding.y; ImGuiButtonFlags button_flags = ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups; if (flags & ImGuiInputTextFlags_ReadOnly) - button_flags |= ImGuiButtonFlags_Disabled; + BeginDisabled(); SameLine(0, style.ItemInnerSpacing.x); if (ButtonEx("-", ImVec2(button_size, button_size), button_flags)) { @@ -3335,6 +3417,8 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data DataTypeApplyOp(data_type, '+', p_data, p_data, g.IO.KeyCtrl && p_step_fast ? p_step_fast : p_step); value_changed = true; } + if (flags & ImGuiInputTextFlags_ReadOnly) + EndDisabled(); const char* label_end = FindRenderedTextEnd(label); if (label != label_end) @@ -3350,10 +3434,10 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data else { if (InputText(label, buf, IM_ARRAYSIZE(buf), flags)) - value_changed = DataTypeApplyOpFromText(buf, g.InputTextState.InitialTextA.Data, data_type, p_data, format); + value_changed = DataTypeApplyFromText(buf, data_type, p_data, format); } if (value_changed) - MarkItemEdited(window->DC.LastItemId); + MarkItemEdited(g.LastItemData.ID); return value_changed; } @@ -3531,12 +3615,12 @@ static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* t namespace ImStb { -static int STB_TEXTEDIT_STRINGLEN(const STB_TEXTEDIT_STRING* obj) { return obj->CurLenW; } -static ImWchar STB_TEXTEDIT_GETCHAR(const STB_TEXTEDIT_STRING* obj, int idx) { return obj->TextW[idx]; } -static float STB_TEXTEDIT_GETWIDTH(STB_TEXTEDIT_STRING* obj, int line_start_idx, int char_idx) { ImWchar c = obj->TextW[line_start_idx + char_idx]; if (c == '\n') return STB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *GImGui; return g.Font->GetCharAdvance(c) * (g.FontSize / g.Font->FontSize); } +static int STB_TEXTEDIT_STRINGLEN(const ImGuiInputTextState* obj) { return obj->CurLenW; } +static ImWchar STB_TEXTEDIT_GETCHAR(const ImGuiInputTextState* obj, int idx) { return obj->TextW[idx]; } +static float STB_TEXTEDIT_GETWIDTH(ImGuiInputTextState* obj, int line_start_idx, int char_idx) { ImWchar c = obj->TextW[line_start_idx + char_idx]; if (c == '\n') return STB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *GImGui; return g.Font->GetCharAdvance(c) * (g.FontSize / g.Font->FontSize); } static int STB_TEXTEDIT_KEYTOTEXT(int key) { return key >= 0x200000 ? 0 : key; } static ImWchar STB_TEXTEDIT_NEWLINE = '\n'; -static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, STB_TEXTEDIT_STRING* obj, int line_start_idx) +static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, ImGuiInputTextState* obj, int line_start_idx) { const ImWchar* text = obj->TextW.Data; const ImWchar* text_remaining = NULL; @@ -3549,19 +3633,21 @@ static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, STB_TEXTEDIT_STRING* ob r->num_chars = (int)(text_remaining - (text + line_start_idx)); } -static bool is_separator(unsigned int c) { return ImCharIsBlankW(c) || c==',' || c==';' || c=='(' || c==')' || c=='{' || c=='}' || c=='[' || c==']' || c=='|'; } -static int is_word_boundary_from_right(STB_TEXTEDIT_STRING* obj, int idx) { return idx > 0 ? (is_separator(obj->TextW[idx - 1]) && !is_separator(obj->TextW[idx]) ) : 1; } -static int STB_TEXTEDIT_MOVEWORDLEFT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx--; while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx--; return idx < 0 ? 0 : idx; } +// When ImGuiInputTextFlags_Password is set, we don't want actions such as CTRL+Arrow to leak the fact that underlying data are blanks or separators. +static bool is_separator(unsigned int c) { return ImCharIsBlankW(c) || c==',' || c==';' || c=='(' || c==')' || c=='{' || c=='}' || c=='[' || c==']' || c=='|' || c=='\n' || c=='\r'; } +static int is_word_boundary_from_right(ImGuiInputTextState* obj, int idx) { if (obj->Flags & ImGuiInputTextFlags_Password) return 0; return idx > 0 ? (is_separator(obj->TextW[idx - 1]) && !is_separator(obj->TextW[idx]) ) : 1; } +static int is_word_boundary_from_left(ImGuiInputTextState* obj, int idx) { if (obj->Flags & ImGuiInputTextFlags_Password) return 0; return idx > 0 ? (!is_separator(obj->TextW[idx - 1]) && is_separator(obj->TextW[idx])) : 1; } +static int STB_TEXTEDIT_MOVEWORDLEFT_IMPL(ImGuiInputTextState* obj, int idx) { idx--; while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx--; return idx < 0 ? 0 : idx; } +static int STB_TEXTEDIT_MOVEWORDRIGHT_MAC(ImGuiInputTextState* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++; return idx > len ? len : idx; } +#define STB_TEXTEDIT_MOVEWORDLEFT STB_TEXTEDIT_MOVEWORDLEFT_IMPL // They need to be #define for stb_textedit.h #ifdef __APPLE__ // FIXME: Move setting to IO structure -static int is_word_boundary_from_left(STB_TEXTEDIT_STRING* obj, int idx) { return idx > 0 ? (!is_separator(obj->TextW[idx - 1]) && is_separator(obj->TextW[idx]) ) : 1; } -static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++; return idx > len ? len : idx; } +#define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_MAC #else -static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; } +static int STB_TEXTEDIT_MOVEWORDRIGHT_WIN(ImGuiInputTextState* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; } +#define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_WIN #endif -#define STB_TEXTEDIT_MOVEWORDLEFT STB_TEXTEDIT_MOVEWORDLEFT_IMPL // They need to be #define for stb_textedit.h -#define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_IMPL -static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING* obj, int pos, int n) +static void STB_TEXTEDIT_DELETECHARS(ImGuiInputTextState* obj, int pos, int n) { ImWchar* dst = obj->TextW.Data + pos; @@ -3577,9 +3663,9 @@ static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING* obj, int pos, int n) *dst = '\0'; } -static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const ImWchar* new_text, int new_text_len) +static bool STB_TEXTEDIT_INSERTCHARS(ImGuiInputTextState* obj, int pos, const ImWchar* new_text, int new_text_len) { - const bool is_resizable = (obj->UserFlags & ImGuiInputTextFlags_CallbackResize) != 0; + const bool is_resizable = (obj->Flags & ImGuiInputTextFlags_CallbackResize) != 0; const int text_len = obj->CurLenW; IM_ASSERT(pos <= text_len); @@ -3633,7 +3719,7 @@ static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const Im // stb_textedit internally allows for a single undo record to do addition and deletion, but somehow, calling // the stb_textedit_paste() function creates two separate records, so we perform it manually. (FIXME: Report to nothings/stb?) -static void stb_textedit_replace(STB_TEXTEDIT_STRING* str, STB_TexteditState* state, const STB_TEXTEDIT_CHARTYPE* text, int text_len) +static void stb_textedit_replace(ImGuiInputTextState* str, STB_TexteditState* state, const STB_TEXTEDIT_CHARTYPE* text, int text_len) { stb_text_makeundo_replace(str, state, 0, str->CurLenW, text_len); ImStb::STB_TEXTEDIT_DELETECHARS(str, 0, str->CurLenW); @@ -3716,11 +3802,13 @@ void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, cons } // Return false to discard a character. -static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) +static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data, ImGuiInputSource input_source) { + IM_ASSERT(input_source == ImGuiInputSource_Keyboard || input_source == ImGuiInputSource_Clipboard); unsigned int c = *p_char; // Filter non-printable (NB: isprint is unreliable! see #2467) + bool apply_named_filters = true; if (c < 0x20) { bool pass = false; @@ -3728,28 +3816,33 @@ static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags f pass |= (c == '\t' && (flags & ImGuiInputTextFlags_AllowTabInput)); if (!pass) return false; + apply_named_filters = false; // Override named filters below so newline and tabs can still be inserted. } - // We ignore Ascii representation of delete (emitted from Backspace on OSX, see #2578, #2817) - if (c == 127) - return false; + if (input_source != ImGuiInputSource_Clipboard) + { + // We ignore Ascii representation of delete (emitted from Backspace on OSX, see #2578, #2817) + if (c == 127) + return false; - // Filter private Unicode range. GLFW on OSX seems to send private characters for special keys like arrow keys (FIXME) - if (c >= 0xE000 && c <= 0xF8FF) - return false; + // Filter private Unicode range. GLFW on OSX seems to send private characters for special keys like arrow keys (FIXME) + if (c >= 0xE000 && c <= 0xF8FF) + return false; + } - // Filter Unicode ranges we are not handling in this build. + // Filter Unicode ranges we are not handling in this build if (c > IM_UNICODE_CODEPOINT_MAX) return false; // Generic named filters - if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_CharsNoBlank | ImGuiInputTextFlags_CharsScientific)) + if (apply_named_filters && (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_CharsNoBlank | ImGuiInputTextFlags_CharsScientific))) { - // The libc allows overriding locale, with e.g. 'setlocale(LC_NUMERIC, "de_DE.UTF-8");' which affect the output/input of printf/scanf. + // The libc allows overriding locale, with e.g. 'setlocale(LC_NUMERIC, "de_DE.UTF-8");' which affect the output/input of printf/scanf to use e.g. ',' instead of '.'. // The standard mandate that programs starts in the "C" locale where the decimal point is '.'. // We don't really intend to provide widespread support for it, but out of empathy for people stuck with using odd API, we support the bare minimum aka overriding the decimal point. // Change the default decimal_point with: // ImGui::GetCurrentContext()->PlatformLocaleDecimalPoint = *localeconv()->decimal_point; + // Users of non-default decimal point (in particular ',') may be affected by word-selection logic (is_word_boundary_from_right/is_word_boundary_from_left) functions. ImGuiContext& g = *GImGui; const unsigned c_decimal_point = (unsigned int)g.PlatformLocaleDecimalPoint; @@ -3828,7 +3921,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if (is_resizable) IM_ASSERT(callback != NULL); // Must provide a callback if you set the ImGuiInputTextFlags_CallbackResize flag! - if (is_multiline) // Open group before calling GetID() because groups tracks id created within their scope, + if (is_multiline) // Open group before calling GetID() because groups tracks id created within their scope (including the scrollbar) BeginGroup(); const ImGuiID id = window->GetID(label); const ImVec2 label_size = CalcTextSize(label, NULL, true); @@ -3840,21 +3933,28 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ ImGuiWindow* draw_window = window; ImVec2 inner_size = frame_size; + ImGuiItemStatusFlags item_status_flags = 0; + ImGuiLastItemData item_data_backup; if (is_multiline) { - if (!ItemAdd(total_bb, id, &frame_bb)) + ImVec2 backup_pos = window->DC.CursorPos; + ItemSize(total_bb, style.FramePadding.y); + if (!ItemAdd(total_bb, id, &frame_bb, ImGuiItemFlags_Inputable)) { - ItemSize(total_bb, style.FramePadding.y); EndGroup(); return false; } + item_status_flags = g.LastItemData.StatusFlags; + item_data_backup = g.LastItemData; + window->DC.CursorPos = backup_pos; // We reproduce the contents of BeginChildFrame() in order to provide 'label' so our window internal data are easier to read/debug. + // FIXME-NAV: Pressing NavActivate will trigger general child activation right before triggering our own below. Harmless but bizarre. PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]); PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding); PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize); - PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding); - bool child_visible = BeginChildEx(label, id, frame_bb.GetSize(), true, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysUseWindowPadding); + PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); // Ensure no clip rect so mouse hover can reach FramePadding edges + bool child_visible = BeginChildEx(label, id, frame_bb.GetSize(), true, ImGuiWindowFlags_NoMove); PopStyleVar(3); PopStyleColor(); if (!child_visible) @@ -3864,14 +3964,18 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ return false; } draw_window = g.CurrentWindow; // Child window - draw_window->DC.NavLayerActiveMaskNext |= (1 << draw_window->DC.NavLayerCurrent); // This is to ensure that EndChild() will display a navigation highlight so we can "enter" into it. + draw_window->DC.NavLayersActiveMaskNext |= (1 << draw_window->DC.NavLayerCurrent); // This is to ensure that EndChild() will display a navigation highlight so we can "enter" into it. + draw_window->DC.CursorPos += style.FramePadding; inner_size.x -= draw_window->ScrollbarSizes.x; } else { + // Support for internal ImGuiInputTextFlags_MergedItem flag, which could be redesigned as an ItemFlags if needed (with test performed in ItemAdd) ItemSize(total_bb, style.FramePadding.y); - if (!ItemAdd(total_bb, id, &frame_bb)) - return false; + if (!(flags & ImGuiInputTextFlags_MergedItem)) + if (!ItemAdd(total_bb, id, &frame_bb, ImGuiItemFlags_Inputable)) + return false; + item_status_flags = g.LastItemData.StatusFlags; } const bool hovered = ItemHoverable(frame_bb, id); if (hovered) @@ -3880,22 +3984,19 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // We are only allowed to access the state if we are already the active widget. ImGuiInputTextState* state = GetInputTextState(id); - const bool focus_requested = FocusableItemRegister(window, id); - const bool focus_requested_by_code = focus_requested && (g.FocusRequestCurrWindow == window && g.FocusRequestCurrCounterRegular == window->DC.FocusCounterRegular); - const bool focus_requested_by_tab = focus_requested && !focus_requested_by_code; + const bool input_requested_by_tabbing = (item_status_flags & ImGuiItemStatusFlags_FocusedByTabbing) != 0; + const bool input_requested_by_nav = (g.ActiveId != id) && ((g.NavActivateInputId == id) || (g.NavActivateId == id && g.NavInputSource == ImGuiInputSource_Keyboard)); const bool user_clicked = hovered && io.MouseClicked[0]; - const bool user_nav_input_start = (g.ActiveId != id) && ((g.NavInputId == id) || (g.NavActivateId == id && g.NavInputSource == ImGuiInputSource_NavKeyboard)); const bool user_scroll_finish = is_multiline && state != NULL && g.ActiveId == 0 && g.ActiveIdPreviousFrame == GetWindowScrollbarID(draw_window, ImGuiAxis_Y); const bool user_scroll_active = is_multiline && state != NULL && g.ActiveId == GetWindowScrollbarID(draw_window, ImGuiAxis_Y); - bool clear_active_id = false; - bool select_all = (g.ActiveId != id) && ((flags & ImGuiInputTextFlags_AutoSelectAll) != 0 || user_nav_input_start) && (!is_multiline); + bool select_all = false; float scroll_y = is_multiline ? draw_window->Scroll.y : FLT_MAX; const bool init_changed_specs = (state != NULL && state->Stb.single_line != !is_multiline); - const bool init_make_active = (focus_requested || user_clicked || user_scroll_finish || user_nav_input_start); + const bool init_make_active = (user_clicked || user_scroll_finish || input_requested_by_nav || input_requested_by_tabbing); const bool init_state = (init_make_active || user_scroll_active); if ((init_state && g.ActiveId != id) || init_changed_specs) { @@ -3931,13 +4032,20 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ state->ID = id; state->ScrollX = 0.0f; stb_textedit_initialize_state(&state->Stb, !is_multiline); - if (!is_multiline && focus_requested_by_code) + } + + if (!is_multiline) + { + if (flags & ImGuiInputTextFlags_AutoSelectAll) + select_all = true; + if (input_requested_by_nav && (!recycle_state || !(g.NavActivateFlags & ImGuiActivateFlags_TryToPreserveState))) + select_all = true; + if (input_requested_by_tabbing || (user_clicked && io.KeyCtrl)) select_all = true; } - if (flags & ImGuiInputTextFlags_AlwaysInsertMode) - state->Stb.insert_mode = 1; - if (!is_multiline && (focus_requested_by_tab || (user_clicked && io.KeyCtrl))) - select_all = true; + + if (flags & ImGuiInputTextFlags_AlwaysOverwrite) + state->Stb.insert_mode = 1; // stb field name is indeed incorrect (see #2863) } if (g.ActiveId != id && init_make_active) @@ -3953,11 +4061,17 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if (is_multiline || (flags & ImGuiInputTextFlags_CallbackHistory)) g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); g.ActiveIdUsingNavInputMask |= (1 << ImGuiNavInput_Cancel); - g.ActiveIdUsingKeyInputMask |= ((ImU64)1 << ImGuiKey_Home) | ((ImU64)1 << ImGuiKey_End); + SetActiveIdUsingKey(ImGuiKey_Home); + SetActiveIdUsingKey(ImGuiKey_End); if (is_multiline) - g.ActiveIdUsingKeyInputMask |= ((ImU64)1 << ImGuiKey_PageUp) | ((ImU64)1 << ImGuiKey_PageDown); - if (flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_AllowTabInput)) // Disable keyboard tabbing out as we will use the \t character. - g.ActiveIdUsingKeyInputMask |= ((ImU64)1 << ImGuiKey_Tab); + { + SetActiveIdUsingKey(ImGuiKey_PageUp); + SetActiveIdUsingKey(ImGuiKey_PageDown); + } + if (flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_AllowTabInput)) // Disable keyboard tabbing out as we will use the \t character. + { + SetActiveIdUsingKey(ImGuiKey_Tab); + } } // We have an edge case if ActiveId was set through another widget (e.g. widget being swapped), clear id immediately (don't wait until the end of the function) @@ -3970,7 +4084,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // Lock the decision of whether we are going to take the path displaying the cursor or selection const bool render_cursor = (g.ActiveId == id) || (state && user_scroll_active); - bool render_selection = state && state->HasSelection() && (RENDER_SELECTION_WHEN_INACTIVE || render_cursor); + bool render_selection = state && (state->HasSelection() || select_all) && (RENDER_SELECTION_WHEN_INACTIVE || render_cursor); bool value_changed = false; bool enter_pressed = false; @@ -4014,9 +4128,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ backup_current_text_length = state->CurLenA; state->Edited = false; state->BufCapacityA = buf_size; - state->UserFlags = flags; - state->UserCallback = callback; - state->UserCallbackData = callback_user_data; + state->Flags = flags; // Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget. // Down the line we should have a cleaner library-wide concept of Selected vs Active. @@ -4025,22 +4137,52 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // Edit in progress const float mouse_x = (io.MousePos.x - frame_bb.Min.x - style.FramePadding.x) + state->ScrollX; - const float mouse_y = (is_multiline ? (io.MousePos.y - draw_window->DC.CursorPos.y - style.FramePadding.y) : (g.FontSize * 0.5f)); + const float mouse_y = (is_multiline ? (io.MousePos.y - draw_window->DC.CursorPos.y) : (g.FontSize * 0.5f)); const bool is_osx = io.ConfigMacOSXBehaviors; - if (select_all || (hovered && !is_osx && io.MouseDoubleClicked[0])) + if (select_all) { state->SelectAll(); state->SelectedAllMouseLock = true; } - else if (hovered && is_osx && io.MouseDoubleClicked[0]) + else if (hovered && io.MouseClickedCount[0] >= 2 && !io.KeyShift) { - // Double-click select a word only, OS X style (by simulating keystrokes) - state->OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT); - state->OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT); + stb_textedit_click(state, &state->Stb, mouse_x, mouse_y); + const int multiclick_count = (io.MouseClickedCount[0] - 2); + if ((multiclick_count % 2) == 0) + { + // Double-click: Select word + // We always use the "Mac" word advance for double-click select vs CTRL+Right which use the platform dependent variant: + // FIXME: There are likely many ways to improve this behavior, but there's no "right" behavior (depends on use-case, software, OS) + const bool is_bol = (state->Stb.cursor == 0) || ImStb::STB_TEXTEDIT_GETCHAR(state, state->Stb.cursor - 1) == '\n'; + if (STB_TEXT_HAS_SELECTION(&state->Stb) || !is_bol) + state->OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT); + //state->OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT); + if (!STB_TEXT_HAS_SELECTION(&state->Stb)) + ImStb::stb_textedit_prep_selection_at_cursor(&state->Stb); + state->Stb.cursor = ImStb::STB_TEXTEDIT_MOVEWORDRIGHT_MAC(state, state->Stb.cursor); + state->Stb.select_end = state->Stb.cursor; + ImStb::stb_textedit_clamp(state, &state->Stb); + } + else + { + // Triple-click: Select line + const bool is_eol = ImStb::STB_TEXTEDIT_GETCHAR(state, state->Stb.cursor) == '\n'; + state->OnKeyPressed(STB_TEXTEDIT_K_LINESTART); + state->OnKeyPressed(STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT); + state->OnKeyPressed(STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT); + if (!is_eol && is_multiline) + { + ImSwap(state->Stb.select_start, state->Stb.select_end); + state->Stb.cursor = state->Stb.select_end; + } + state->CursorFollow = false; + } + state->CursorAnimReset(); } else if (io.MouseClicked[0] && !state->SelectedAllMouseLock) { + // FIXME: unselect on late click could be done release? if (hovered) { stb_textedit_click(state, &state->Stb, mouse_x, mouse_y); @@ -4059,11 +4201,11 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // It is ill-defined whether the backend needs to send a \t character when pressing the TAB keys. // Win32 and GLFW naturally do it but not SDL. const bool ignore_char_inputs = (io.KeyCtrl && !io.KeyAlt) || (is_osx && io.KeySuper); - if ((flags & ImGuiInputTextFlags_AllowTabInput) && IsKeyPressedMap(ImGuiKey_Tab) && !ignore_char_inputs && !io.KeyShift && !is_readonly) + if ((flags & ImGuiInputTextFlags_AllowTabInput) && IsKeyPressed(ImGuiKey_Tab) && !ignore_char_inputs && !io.KeyShift && !is_readonly) if (!io.InputQueueCharacters.contains('\t')) { unsigned int c = '\t'; // Insert TAB - if (InputTextFilterCharacter(&c, flags, callback, callback_user_data)) + if (InputTextFilterCharacter(&c, flags, callback, callback_user_data, ImGuiInputSource_Keyboard)) state->OnKeyPressed((int)c); } @@ -4071,14 +4213,14 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // We ignore CTRL inputs, but need to allow ALT+CTRL as some keyboards (e.g. German) use AltGR (which _is_ Alt+Ctrl) to input certain characters. if (io.InputQueueCharacters.Size > 0) { - if (!ignore_char_inputs && !is_readonly && !user_nav_input_start) + if (!ignore_char_inputs && !is_readonly && !input_requested_by_nav) for (int n = 0; n < io.InputQueueCharacters.Size; n++) { // Insert character if they pass filtering unsigned int c = (unsigned int)io.InputQueueCharacters[n]; if (c == '\t' && io.KeyShift) continue; - if (InputTextFilterCharacter(&c, flags, callback, callback_user_data)) + if (InputTextFilterCharacter(&c, flags, callback, callback_user_data, ImGuiInputSource_Keyboard)) state->OnKeyPressed((int)c); } @@ -4106,22 +4248,27 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ const bool is_shift_key_only = (io.KeyMods == ImGuiKeyModFlags_Shift); const bool is_shortcut_key = g.IO.ConfigMacOSXBehaviors ? (io.KeyMods == ImGuiKeyModFlags_Super) : (io.KeyMods == ImGuiKeyModFlags_Ctrl); - const bool is_cut = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_X)) || (is_shift_key_only && IsKeyPressedMap(ImGuiKey_Delete))) && !is_readonly && !is_password && (!is_multiline || state->HasSelection()); - const bool is_copy = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_C)) || (is_ctrl_key_only && IsKeyPressedMap(ImGuiKey_Insert))) && !is_password && (!is_multiline || state->HasSelection()); - const bool is_paste = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_V)) || (is_shift_key_only && IsKeyPressedMap(ImGuiKey_Insert))) && !is_readonly; - const bool is_undo = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_Z)) && !is_readonly && is_undoable); - const bool is_redo = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_Y)) || (is_osx_shift_shortcut && IsKeyPressedMap(ImGuiKey_Z))) && !is_readonly && is_undoable; - - if (IsKeyPressedMap(ImGuiKey_LeftArrow)) { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINESTART : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDLEFT : STB_TEXTEDIT_K_LEFT) | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_RightArrow)) { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINEEND : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDRIGHT : STB_TEXTEDIT_K_RIGHT) | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_UpArrow) && is_multiline) { if (io.KeyCtrl) SetScrollY(draw_window, ImMax(draw_window->Scroll.y - g.FontSize, 0.0f)); else state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTSTART : STB_TEXTEDIT_K_UP) | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_DownArrow) && is_multiline) { if (io.KeyCtrl) SetScrollY(draw_window, ImMin(draw_window->Scroll.y + g.FontSize, GetScrollMaxY())); else state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTEND : STB_TEXTEDIT_K_DOWN) | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_PageUp) && is_multiline) { state->OnKeyPressed(STB_TEXTEDIT_K_PGUP | k_mask); scroll_y -= row_count_per_page * g.FontSize; } - else if (IsKeyPressedMap(ImGuiKey_PageDown) && is_multiline) { state->OnKeyPressed(STB_TEXTEDIT_K_PGDOWN | k_mask); scroll_y += row_count_per_page * g.FontSize; } - else if (IsKeyPressedMap(ImGuiKey_Home)) { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_End)) { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTEND | k_mask : STB_TEXTEDIT_K_LINEEND | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_Delete) && !is_readonly) { state->OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_Backspace) && !is_readonly) + const bool is_cut = ((is_shortcut_key && IsKeyPressed(ImGuiKey_X)) || (is_shift_key_only && IsKeyPressed(ImGuiKey_Delete))) && !is_readonly && !is_password && (!is_multiline || state->HasSelection()); + const bool is_copy = ((is_shortcut_key && IsKeyPressed(ImGuiKey_C)) || (is_ctrl_key_only && IsKeyPressed(ImGuiKey_Insert))) && !is_password && (!is_multiline || state->HasSelection()); + const bool is_paste = ((is_shortcut_key && IsKeyPressed(ImGuiKey_V)) || (is_shift_key_only && IsKeyPressed(ImGuiKey_Insert))) && !is_readonly; + const bool is_undo = ((is_shortcut_key && IsKeyPressed(ImGuiKey_Z)) && !is_readonly && is_undoable); + const bool is_redo = ((is_shortcut_key && IsKeyPressed(ImGuiKey_Y)) || (is_osx_shift_shortcut && IsKeyPressed(ImGuiKey_Z))) && !is_readonly && is_undoable; + + // We allow validate/cancel with Nav source (gamepad) to makes it easier to undo an accidental NavInput press with no keyboard wired, but otherwise it isn't very useful. + const bool is_validate_enter = IsKeyPressed(ImGuiKey_Enter) || IsKeyPressed(ImGuiKey_KeypadEnter); + const bool is_validate_nav = (IsNavInputTest(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed) && !IsKeyPressed(ImGuiKey_Space)) || IsNavInputTest(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed); + const bool is_cancel = IsKeyPressed(ImGuiKey_Escape) || IsNavInputTest(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed); + + if (IsKeyPressed(ImGuiKey_LeftArrow)) { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINESTART : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDLEFT : STB_TEXTEDIT_K_LEFT) | k_mask); } + else if (IsKeyPressed(ImGuiKey_RightArrow)) { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINEEND : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDRIGHT : STB_TEXTEDIT_K_RIGHT) | k_mask); } + else if (IsKeyPressed(ImGuiKey_UpArrow) && is_multiline) { if (io.KeyCtrl) SetScrollY(draw_window, ImMax(draw_window->Scroll.y - g.FontSize, 0.0f)); else state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTSTART : STB_TEXTEDIT_K_UP) | k_mask); } + else if (IsKeyPressed(ImGuiKey_DownArrow) && is_multiline) { if (io.KeyCtrl) SetScrollY(draw_window, ImMin(draw_window->Scroll.y + g.FontSize, GetScrollMaxY())); else state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTEND : STB_TEXTEDIT_K_DOWN) | k_mask); } + else if (IsKeyPressed(ImGuiKey_PageUp) && is_multiline) { state->OnKeyPressed(STB_TEXTEDIT_K_PGUP | k_mask); scroll_y -= row_count_per_page * g.FontSize; } + else if (IsKeyPressed(ImGuiKey_PageDown) && is_multiline) { state->OnKeyPressed(STB_TEXTEDIT_K_PGDOWN | k_mask); scroll_y += row_count_per_page * g.FontSize; } + else if (IsKeyPressed(ImGuiKey_Home)) { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask); } + else if (IsKeyPressed(ImGuiKey_End)) { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTEND | k_mask : STB_TEXTEDIT_K_LINEEND | k_mask); } + else if (IsKeyPressed(ImGuiKey_Delete) && !is_readonly && !is_cut) { state->OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask); } + else if (IsKeyPressed(ImGuiKey_Backspace) && !is_readonly) { if (!state->HasSelection()) { @@ -4132,7 +4279,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ } state->OnKeyPressed(STB_TEXTEDIT_K_BACKSPACE | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_Enter) || IsKeyPressedMap(ImGuiKey_KeyPadEnter)) + else if (is_validate_enter) { bool ctrl_enter_for_new_line = (flags & ImGuiInputTextFlags_CtrlEnterForNewLine) != 0; if (!is_multiline || (ctrl_enter_for_new_line && !io.KeyCtrl) || (!ctrl_enter_for_new_line && io.KeyCtrl)) @@ -4142,11 +4289,16 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ else if (!is_readonly) { unsigned int c = '\n'; // Insert new line - if (InputTextFilterCharacter(&c, flags, callback, callback_user_data)) + if (InputTextFilterCharacter(&c, flags, callback, callback_user_data, ImGuiInputSource_Keyboard)) state->OnKeyPressed((int)c); } } - else if (IsKeyPressedMap(ImGuiKey_Escape)) + else if (is_validate_nav) + { + IM_ASSERT(!is_validate_enter); + enter_pressed = clear_active_id = true; + } + else if (is_cancel) { clear_active_id = cancel_edit = true; } @@ -4155,7 +4307,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ state->OnKeyPressed(is_undo ? STB_TEXTEDIT_K_UNDO : STB_TEXTEDIT_K_REDO); state->ClearSelection(); } - else if (is_shortcut_key && IsKeyPressedMap(ImGuiKey_A)) + else if (is_shortcut_key && IsKeyPressed(ImGuiKey_A)) { state->SelectAll(); state->CursorFollow = true; @@ -4195,7 +4347,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ s += ImTextCharFromUtf8(&c, s, NULL); if (c == 0) break; - if (!InputTextFilterCharacter(&c, flags, callback, callback_user_data)) + if (!InputTextFilterCharacter(&c, flags, callback, callback_user_data, ImGuiInputSource_Clipboard)) continue; clipboard_filtered[clipboard_filtered_len++] = (ImWchar)c; } @@ -4214,11 +4366,11 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ } // Process callbacks and apply result back to user's buffer. + const char* apply_new_text = NULL; + int apply_new_text_length = 0; if (g.ActiveId == id) { IM_ASSERT(state != NULL); - const char* apply_new_text = NULL; - int apply_new_text_length = 0; if (cancel_edit) { // Restore initial value. Only return true if restoring to the initial value changes the current buffer contents. @@ -4261,18 +4413,18 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // The reason we specify the usage semantic (Completion/History) is that Completion needs to disable keyboard TABBING at the moment. ImGuiInputTextFlags event_flag = 0; - ImGuiKey event_key = ImGuiKey_COUNT; - if ((flags & ImGuiInputTextFlags_CallbackCompletion) != 0 && IsKeyPressedMap(ImGuiKey_Tab)) + ImGuiKey event_key = ImGuiKey_None; + if ((flags & ImGuiInputTextFlags_CallbackCompletion) != 0 && IsKeyPressed(ImGuiKey_Tab)) { event_flag = ImGuiInputTextFlags_CallbackCompletion; event_key = ImGuiKey_Tab; } - else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_UpArrow)) + else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressed(ImGuiKey_UpArrow)) { event_flag = ImGuiInputTextFlags_CallbackHistory; event_key = ImGuiKey_UpArrow; } - else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_DownArrow)) + else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressed(ImGuiKey_DownArrow)) { event_flag = ImGuiInputTextFlags_CallbackHistory; event_key = ImGuiKey_DownArrow; @@ -4294,8 +4446,9 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ callback_data.Flags = flags; callback_data.UserData = callback_user_data; + char* callback_buf = is_readonly ? buf : state->TextA.Data; callback_data.EventKey = event_key; - callback_data.Buf = state->TextA.Data; + callback_data.Buf = callback_buf; callback_data.BufTextLen = state->CurLenA; callback_data.BufSize = state->BufCapacityA; callback_data.BufDirty = false; @@ -4310,7 +4463,8 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ callback(&callback_data); // Read back what user may have modified - IM_ASSERT(callback_data.Buf == state->TextA.Data); // Invalid to modify those fields + callback_buf = is_readonly ? buf : state->TextA.Data; // Pointer may have been invalidated by a resize callback + IM_ASSERT(callback_data.Buf == callback_buf); // Invalid to modify those fields IM_ASSERT(callback_data.BufSize == state->BufCapacityA); IM_ASSERT(callback_data.Flags == flags); const bool buf_dirty = callback_data.BufDirty; @@ -4337,39 +4491,37 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ } } - // Copy result to user buffer - if (apply_new_text) - { - // We cannot test for 'backup_current_text_length != apply_new_text_length' here because we have no guarantee that the size - // of our owned buffer matches the size of the string object held by the user, and by design we allow InputText() to be used - // without any storage on user's side. - IM_ASSERT(apply_new_text_length >= 0); - if (is_resizable) - { - ImGuiInputTextCallbackData callback_data; - callback_data.EventFlag = ImGuiInputTextFlags_CallbackResize; - callback_data.Flags = flags; - callback_data.Buf = buf; - callback_data.BufTextLen = apply_new_text_length; - callback_data.BufSize = ImMax(buf_size, apply_new_text_length + 1); - callback_data.UserData = callback_user_data; - callback(&callback_data); - buf = callback_data.Buf; - buf_size = callback_data.BufSize; - apply_new_text_length = ImMin(callback_data.BufTextLen, buf_size - 1); - IM_ASSERT(apply_new_text_length <= buf_size); - } - //IMGUI_DEBUG_LOG("InputText(\"%s\"): apply_new_text length %d\n", label, apply_new_text_length); + // Clear temporary user storage + state->Flags = ImGuiInputTextFlags_None; + } - // If the underlying buffer resize was denied or not carried to the next frame, apply_new_text_length+1 may be >= buf_size. - ImStrncpy(buf, apply_new_text, ImMin(apply_new_text_length + 1, buf_size)); - value_changed = true; + // Copy result to user buffer. This can currently only happen when (g.ActiveId == id) + if (apply_new_text != NULL) + { + // We cannot test for 'backup_current_text_length != apply_new_text_length' here because we have no guarantee that the size + // of our owned buffer matches the size of the string object held by the user, and by design we allow InputText() to be used + // without any storage on user's side. + IM_ASSERT(apply_new_text_length >= 0); + if (is_resizable) + { + ImGuiInputTextCallbackData callback_data; + callback_data.EventFlag = ImGuiInputTextFlags_CallbackResize; + callback_data.Flags = flags; + callback_data.Buf = buf; + callback_data.BufTextLen = apply_new_text_length; + callback_data.BufSize = ImMax(buf_size, apply_new_text_length + 1); + callback_data.UserData = callback_user_data; + callback(&callback_data); + buf = callback_data.Buf; + buf_size = callback_data.BufSize; + apply_new_text_length = ImMin(callback_data.BufTextLen, buf_size - 1); + IM_ASSERT(apply_new_text_length <= buf_size); } + //IMGUI_DEBUG_LOG("InputText(\"%s\"): apply_new_text length %d\n", label, apply_new_text_length); - // Clear temporary user storage - state->UserFlags = 0; - state->UserCallback = NULL; - state->UserCallbackData = NULL; + // If the underlying buffer resize was denied or not carried to the next frame, apply_new_text_length+1 may be >= buf_size. + ImStrncpy(buf, apply_new_text, ImMin(apply_new_text_length + 1, buf_size)); + value_changed = true; } // Release active ID at the end of the function (so e.g. pressing Return still does a final application of the value) @@ -4474,10 +4626,11 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if (!(flags & ImGuiInputTextFlags_NoHorizontalScroll)) { const float scroll_increment_x = inner_size.x * 0.25f; + const float visible_width = inner_size.x - style.FramePadding.x; if (cursor_offset.x < state->ScrollX) state->ScrollX = IM_FLOOR(ImMax(0.0f, cursor_offset.x - scroll_increment_x)); - else if (cursor_offset.x - inner_size.x >= state->ScrollX) - state->ScrollX = IM_FLOOR(cursor_offset.x - inner_size.x + scroll_increment_x); + else if (cursor_offset.x - visible_width >= state->ScrollX) + state->ScrollX = IM_FLOOR(cursor_offset.x - visible_width + scroll_increment_x); } else { @@ -4490,7 +4643,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // Test if cursor is vertically visible if (cursor_offset.y - g.FontSize < scroll_y) scroll_y = ImMax(0.0f, cursor_offset.y - g.FontSize); - else if (cursor_offset.y - inner_size.y >= scroll_y) + else if (cursor_offset.y - (inner_size.y - style.FramePadding.y * 2.0f) >= scroll_y) scroll_y = cursor_offset.y - inner_size.y + style.FramePadding.y * 2.0f; const float scroll_max_y = ImMax((text_size.y + style.FramePadding.y * 2.0f) - inner_size.y, 0.0f); scroll_y = ImClamp(scroll_y, 0.0f, scroll_max_y); @@ -4550,14 +4703,18 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ { state->CursorAnim += io.DeltaTime; bool cursor_is_visible = (!g.IO.ConfigInputTextCursorBlink) || (state->CursorAnim <= 0.0f) || ImFmod(state->CursorAnim, 1.20f) <= 0.80f; - ImVec2 cursor_screen_pos = draw_pos + cursor_offset - draw_scroll; + ImVec2 cursor_screen_pos = ImFloor(draw_pos + cursor_offset - draw_scroll); ImRect cursor_screen_rect(cursor_screen_pos.x, cursor_screen_pos.y - g.FontSize + 0.5f, cursor_screen_pos.x + 1.0f, cursor_screen_pos.y - 1.5f); if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect)) draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_Text)); // Notify OS of text input position for advanced IME (-1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety.) if (!is_readonly) - g.PlatformImePos = ImVec2(cursor_screen_pos.x - 1.0f, cursor_screen_pos.y - g.FontSize); + { + g.PlatformImeData.WantVisible = true; + g.PlatformImeData.InputPos = ImVec2(cursor_screen_pos.x - 1.0f, cursor_screen_pos.y - g.FontSize); + g.PlatformImeData.InputLineHeight = g.FontSize; + } } } else @@ -4582,14 +4739,31 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if (is_multiline) { - Dummy(text_size); + // For focus requests to work on our multiline we need to ensure our child ItemAdd() call specifies the ImGuiItemFlags_Inputable (ref issue #4761)... + Dummy(ImVec2(text_size.x, text_size.y + style.FramePadding.y)); + ImGuiItemFlags backup_item_flags = g.CurrentItemFlags; + g.CurrentItemFlags |= ImGuiItemFlags_Inputable | ImGuiItemFlags_NoTabStop; EndChild(); + item_data_backup.StatusFlags |= (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredWindow); + g.CurrentItemFlags = backup_item_flags; + + // ...and then we need to undo the group overriding last item data, which gets a bit messy as EndGroup() tries to forward scrollbar being active... + // FIXME: This quite messy/tricky, should attempt to get rid of the child window. EndGroup(); + if (g.LastItemData.ID == 0) + { + g.LastItemData.ID = id; + g.LastItemData.InFlags = item_data_backup.InFlags; + g.LastItemData.StatusFlags = item_data_backup.StatusFlags; + } } // Log as text if (g.LogEnabled && (!is_password || is_displaying_hint)) + { + LogSetNextTextDecoration("{", "}"); LogRenderedText(&draw_pos, buf_display, buf_display_end); + } if (label_size.x > 0) RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); @@ -4597,7 +4771,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if (value_changed && !(flags & ImGuiInputTextFlags_NoMarkEdited)) MarkItemEdited(id); - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags); + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); if ((flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0) return enter_pressed; else @@ -4624,6 +4798,30 @@ bool ImGui::ColorEdit3(const char* label, float col[3], ImGuiColorEditFlags flag return ColorEdit4(label, col, flags | ImGuiColorEditFlags_NoAlpha); } +// ColorEdit supports RGB and HSV inputs. In case of RGB input resulting color may have undefined hue and/or saturation. +// Since widget displays both RGB and HSV values we must preserve hue and saturation to prevent these values resetting. +static void ColorEditRestoreHS(const float* col, float* H, float* S, float* V) +{ + // This check is optional. Suppose we have two color widgets side by side, both widgets display different colors, but both colors have hue and/or saturation undefined. + // With color check: hue/saturation is preserved in one widget. Editing color in one widget would reset hue/saturation in another one. + // Without color check: common hue/saturation would be displayed in all widgets that have hue/saturation undefined. + // g.ColorEditLastColor is stored as ImU32 RGB value: this essentially gives us color equality check with reduced precision. + // Tiny external color changes would not be detected and this check would still pass. This is OK, since we only restore hue/saturation _only_ if they are undefined, + // therefore this change flipping hue/saturation from undefined to a very tiny value would still be represented in color picker. + ImGuiContext& g = *GImGui; + if (g.ColorEditLastColor != ImGui::ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 0))) + return; + + // When S == 0, H is undefined. + // When H == 1 it wraps around to 0. + if (*S == 0.0f || (*H == 0.0f && g.ColorEditLastHue == 1)) + *H = g.ColorEditLastHue; + + // When V == 0, S is undefined. + if (*V == 0.0f) + *S = g.ColorEditLastSat; +} + // Edit colors components (each component in 0.0f..1.0f range). // See enum ImGuiColorEditFlags_ for available options. e.g. Only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set. // With typical options: Left-click on color square to open color picker. Right-click to open option menu. CTRL-Click over input fields to edit them and TAB to go to next item. @@ -4648,24 +4846,24 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag // If we're not showing any slider there's no point in doing any HSV conversions const ImGuiColorEditFlags flags_untouched = flags; if (flags & ImGuiColorEditFlags_NoInputs) - flags = (flags & (~ImGuiColorEditFlags__DisplayMask)) | ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_NoOptions; + flags = (flags & (~ImGuiColorEditFlags_DisplayMask_)) | ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_NoOptions; // Context menu: display and modify options (before defaults are applied) if (!(flags & ImGuiColorEditFlags_NoOptions)) ColorEditOptionsPopup(col, flags); // Read stored options - if (!(flags & ImGuiColorEditFlags__DisplayMask)) - flags |= (g.ColorEditOptions & ImGuiColorEditFlags__DisplayMask); - if (!(flags & ImGuiColorEditFlags__DataTypeMask)) - flags |= (g.ColorEditOptions & ImGuiColorEditFlags__DataTypeMask); - if (!(flags & ImGuiColorEditFlags__PickerMask)) - flags |= (g.ColorEditOptions & ImGuiColorEditFlags__PickerMask); - if (!(flags & ImGuiColorEditFlags__InputMask)) - flags |= (g.ColorEditOptions & ImGuiColorEditFlags__InputMask); - flags |= (g.ColorEditOptions & ~(ImGuiColorEditFlags__DisplayMask | ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__PickerMask | ImGuiColorEditFlags__InputMask)); - IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags__DisplayMask)); // Check that only 1 is selected - IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags__InputMask)); // Check that only 1 is selected + if (!(flags & ImGuiColorEditFlags_DisplayMask_)) + flags |= (g.ColorEditOptions & ImGuiColorEditFlags_DisplayMask_); + if (!(flags & ImGuiColorEditFlags_DataTypeMask_)) + flags |= (g.ColorEditOptions & ImGuiColorEditFlags_DataTypeMask_); + if (!(flags & ImGuiColorEditFlags_PickerMask_)) + flags |= (g.ColorEditOptions & ImGuiColorEditFlags_PickerMask_); + if (!(flags & ImGuiColorEditFlags_InputMask_)) + flags |= (g.ColorEditOptions & ImGuiColorEditFlags_InputMask_); + flags |= (g.ColorEditOptions & ~(ImGuiColorEditFlags_DisplayMask_ | ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_PickerMask_ | ImGuiColorEditFlags_InputMask_)); + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_DisplayMask_)); // Check that only 1 is selected + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_InputMask_)); // Check that only 1 is selected const bool alpha = (flags & ImGuiColorEditFlags_NoAlpha) == 0; const bool hdr = (flags & ImGuiColorEditFlags_HDR) != 0; @@ -4679,13 +4877,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag { // Hue is lost when converting from greyscale rgb (saturation=0). Restore it. ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]); - if (memcmp(g.ColorEditLastColor, col, sizeof(float) * 3) == 0) - { - if (f[1] == 0) - f[0] = g.ColorEditLastHue; - if (f[2] == 0) - f[1] = g.ColorEditLastSat; - } + ColorEditRestoreHS(col, &f[0], &f[1], &f[2]); } int i[4] = { IM_F32_TO_INT8_UNBOUND(f[0]), IM_F32_TO_INT8_UNBOUND(f[1]), IM_F32_TO_INT8_UNBOUND(f[2]), IM_F32_TO_INT8_UNBOUND(f[3]) }; @@ -4735,7 +4927,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag value_changed |= DragInt(ids[n], &i[n], 1.0f, 0, hdr ? 0 : 255, fmt_table_int[fmt_idx][n]); } if (!(flags & ImGuiColorEditFlags_NoOptions)) - OpenPopupOnItemClick("context"); + OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight); } } else if ((flags & ImGuiColorEditFlags_DisplayHex) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0) @@ -4753,14 +4945,17 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag char* p = buf; while (*p == '#' || ImCharIsBlankA(*p)) p++; - i[0] = i[1] = i[2] = i[3] = 0; + i[0] = i[1] = i[2] = 0; + i[3] = 0xFF; // alpha default to 255 is not parsed by scanf (e.g. inputting #FFFFFF omitting alpha) + int r; if (alpha) - sscanf(p, "%02X%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2], (unsigned int*)&i[3]); // Treat at unsigned (%X is unsigned) + r = sscanf(p, "%02X%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2], (unsigned int*)&i[3]); // Treat at unsigned (%X is unsigned) else - sscanf(p, "%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2]); + r = sscanf(p, "%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2]); + IM_UNUSED(r); // Fixes C6031: Return value ignored: 'sscanf'. } if (!(flags & ImGuiColorEditFlags_NoOptions)) - OpenPopupOnItemClick("context"); + OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight); } ImGuiWindow* picker_active_window = NULL; @@ -4777,11 +4972,11 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag // Store current color and open a picker g.ColorPickerRef = col_v4; OpenPopup("picker"); - SetNextWindowPos(window->DC.LastItemRect.GetBL() + ImVec2(-1, style.ItemSpacing.y)); + SetNextWindowPos(g.LastItemData.Rect.GetBL() + ImVec2(0.0f, style.ItemSpacing.y)); } } if (!(flags & ImGuiColorEditFlags_NoOptions)) - OpenPopupOnItemClick("context"); + OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight); if (BeginPopup("picker")) { @@ -4791,8 +4986,8 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag TextEx(label, label_display_end); Spacing(); } - ImGuiColorEditFlags picker_flags_to_forward = ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__PickerMask | ImGuiColorEditFlags__InputMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaBar; - ImGuiColorEditFlags picker_flags = (flags_untouched & picker_flags_to_forward) | ImGuiColorEditFlags__DisplayMask | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_AlphaPreviewHalf; + ImGuiColorEditFlags picker_flags_to_forward = ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_PickerMask_ | ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaBar; + ImGuiColorEditFlags picker_flags = (flags_untouched & picker_flags_to_forward) | ImGuiColorEditFlags_DisplayMask_ | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_AlphaPreviewHalf; SetNextItemWidth(square_sz * 12.0f); // Use 256 + bar sizes? value_changed |= ColorPicker4("##picker", col, picker_flags, &g.ColorPickerRef.x); EndPopup(); @@ -4817,7 +5012,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag g.ColorEditLastHue = f[0]; g.ColorEditLastSat = f[1]; ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]); - memcpy(g.ColorEditLastColor, f, sizeof(float) * 3); + g.ColorEditLastColor = ColorConvertFloat4ToU32(ImVec4(f[0], f[1], f[2], 0)); } if ((flags & ImGuiColorEditFlags_DisplayRGB) && (flags & ImGuiColorEditFlags_InputHSV)) ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]); @@ -4834,7 +5029,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag // Drag and Drop Target // NB: The flag test is merely an optional micro-optimization, BeginDragDropTarget() does the same test. - if ((window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) && !(flags & ImGuiColorEditFlags_NoDragDrop) && BeginDragDropTarget()) + if ((g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) && !(flags & ImGuiColorEditFlags_NoDragDrop) && BeginDragDropTarget()) { bool accepted_drag_drop = false; if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F)) @@ -4856,10 +5051,10 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag // When picker is being actively used, use its active id so IsItemActive() will function on ColorEdit4(). if (picker_active_window && g.ActiveId != 0 && g.ActiveIdWindow == picker_active_window) - window->DC.LastItemId = g.ActiveId; + g.LastItemData.ID = g.ActiveId; if (value_changed) - MarkItemEdited(window->DC.LastItemId); + MarkItemEdited(g.LastItemData.ID); return value_changed; } @@ -4912,12 +5107,12 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl ColorPickerOptionsPopup(col, flags); // Read stored options - if (!(flags & ImGuiColorEditFlags__PickerMask)) - flags |= ((g.ColorEditOptions & ImGuiColorEditFlags__PickerMask) ? g.ColorEditOptions : ImGuiColorEditFlags__OptionsDefault) & ImGuiColorEditFlags__PickerMask; - if (!(flags & ImGuiColorEditFlags__InputMask)) - flags |= ((g.ColorEditOptions & ImGuiColorEditFlags__InputMask) ? g.ColorEditOptions : ImGuiColorEditFlags__OptionsDefault) & ImGuiColorEditFlags__InputMask; - IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags__PickerMask)); // Check that only 1 is selected - IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags__InputMask)); // Check that only 1 is selected + if (!(flags & ImGuiColorEditFlags_PickerMask_)) + flags |= ((g.ColorEditOptions & ImGuiColorEditFlags_PickerMask_) ? g.ColorEditOptions : ImGuiColorEditFlags_DefaultOptions_) & ImGuiColorEditFlags_PickerMask_; + if (!(flags & ImGuiColorEditFlags_InputMask_)) + flags |= ((g.ColorEditOptions & ImGuiColorEditFlags_InputMask_) ? g.ColorEditOptions : ImGuiColorEditFlags_DefaultOptions_) & ImGuiColorEditFlags_InputMask_; + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_PickerMask_)); // Check that only 1 is selected + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_InputMask_)); // Check that only 1 is selected if (!(flags & ImGuiColorEditFlags_NoOptions)) flags |= (g.ColorEditOptions & ImGuiColorEditFlags_AlphaBar); @@ -4952,13 +5147,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl { // Hue is lost when converting from greyscale rgb (saturation=0). Restore it. ColorConvertRGBtoHSV(R, G, B, H, S, V); - if (memcmp(g.ColorEditLastColor, col, sizeof(float) * 3) == 0) - { - if (S == 0) - H = g.ColorEditLastHue; - if (V == 0) - S = g.ColorEditLastSat; - } + ColorEditRestoreHS(col, &H, &S, &V); } else if (flags & ImGuiColorEditFlags_InputHSV) { @@ -5001,7 +5190,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl } } if (!(flags & ImGuiColorEditFlags_NoOptions)) - OpenPopupOnItemClick("context"); + OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight); } else if (flags & ImGuiColorEditFlags_PickerHueBar) { @@ -5011,10 +5200,14 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl { S = ImSaturate((io.MousePos.x - picker_pos.x) / (sv_picker_size - 1)); V = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size - 1)); + + // Greatly reduces hue jitter and reset to 0 when hue == 255 and color is rapidly modified using SV square. + if (g.ColorEditLastColor == ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 0))) + H = g.ColorEditLastHue; value_changed = value_changed_sv = true; } if (!(flags & ImGuiColorEditFlags_NoOptions)) - OpenPopupOnItemClick("context"); + OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight); // Hue bar logic SetCursorScreenPos(ImVec2(bar0_pos_x, picker_pos.y)); @@ -5063,7 +5256,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl if ((flags & ImGuiColorEditFlags_NoLabel)) Text("Current"); - ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags__InputMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf | ImGuiColorEditFlags_NoTooltip; + ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf | ImGuiColorEditFlags_NoTooltip; ColorButton("##current", col_v4, (flags & sub_flags_to_forward), ImVec2(square_sz * 3, square_sz * 2)); if (ref_col != NULL) { @@ -5084,10 +5277,10 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl { if (flags & ImGuiColorEditFlags_InputRGB) { - ColorConvertHSVtoRGB(H >= 1.0f ? H - 10 * 1e-6f : H, S > 0.0f ? S : 10 * 1e-6f, V > 0.0f ? V : 1e-6f, col[0], col[1], col[2]); + ColorConvertHSVtoRGB(H, S, V, col[0], col[1], col[2]); g.ColorEditLastHue = H; g.ColorEditLastSat = S; - memcpy(g.ColorEditLastColor, col, sizeof(float) * 3); + g.ColorEditLastColor = ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 0)); } else if (flags & ImGuiColorEditFlags_InputHSV) { @@ -5102,9 +5295,9 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl if ((flags & ImGuiColorEditFlags_NoInputs) == 0) { PushItemWidth((alpha_bar ? bar1_pos_x : bar0_pos_x) + bars_width - picker_pos.x); - ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__InputMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoSmallPreview | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf; + ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoSmallPreview | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf; ImGuiColorEditFlags sub_flags = (flags & sub_flags_to_forward) | ImGuiColorEditFlags_NoPicker; - if (flags & ImGuiColorEditFlags_DisplayRGB || (flags & ImGuiColorEditFlags__DisplayMask) == 0) + if (flags & ImGuiColorEditFlags_DisplayRGB || (flags & ImGuiColorEditFlags_DisplayMask_) == 0) if (ColorEdit4("##rgb", col, sub_flags | ImGuiColorEditFlags_DisplayRGB)) { // FIXME: Hackily differentiating using the DragInt (ActiveId != 0 && !ActiveIdAllowOverlap) vs. using the InputText or DropTarget. @@ -5112,9 +5305,9 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl value_changed_fix_hue_wrap = (g.ActiveId != 0 && !g.ActiveIdAllowOverlap); value_changed = true; } - if (flags & ImGuiColorEditFlags_DisplayHSV || (flags & ImGuiColorEditFlags__DisplayMask) == 0) + if (flags & ImGuiColorEditFlags_DisplayHSV || (flags & ImGuiColorEditFlags_DisplayMask_) == 0) value_changed |= ColorEdit4("##hsv", col, sub_flags | ImGuiColorEditFlags_DisplayHSV); - if (flags & ImGuiColorEditFlags_DisplayHex || (flags & ImGuiColorEditFlags__DisplayMask) == 0) + if (flags & ImGuiColorEditFlags_DisplayHex || (flags & ImGuiColorEditFlags_DisplayMask_) == 0) value_changed |= ColorEdit4("##hex", col, sub_flags | ImGuiColorEditFlags_DisplayHex); PopItemWidth(); } @@ -5141,13 +5334,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl G = col[1]; B = col[2]; ColorConvertRGBtoHSV(R, G, B, H, S, V); - if (memcmp(g.ColorEditLastColor, col, sizeof(float) * 3) == 0) // Fix local Hue as display below will use it immediately. - { - if (S == 0) - H = g.ColorEditLastHue; - if (V == 0) - S = g.ColorEditLastSat; - } + ColorEditRestoreHS(col, &H, &S, &V); // Fix local Hue as display below will use it immediately. } else if (flags & ImGuiColorEditFlags_InputHSV) { @@ -5181,7 +5368,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl const float a1 = (n+1.0f)/6.0f * 2.0f * IM_PI + aeps; const int vert_start_idx = draw_list->VtxBuffer.Size; draw_list->PathArcTo(wheel_center, (wheel_r_inner + wheel_r_outer)*0.5f, a0, a1, segment_per_arc); - draw_list->PathStroke(col_white, false, wheel_thickness); + draw_list->PathStroke(col_white, 0, wheel_thickness); const int vert_end_idx = draw_list->VtxBuffer.Size; // Paint colors over existing vertices @@ -5255,7 +5442,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl if (value_changed && memcmp(backup_initial_col, col, components * sizeof(float)) == 0) value_changed = false; if (value_changed) - MarkItemEdited(window->DC.LastItemId); + MarkItemEdited(g.LastItemData.ID); PopID(); @@ -5307,8 +5494,8 @@ bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFl if ((flags & ImGuiColorEditFlags_AlphaPreviewHalf) && col_rgb.w < 1.0f) { float mid_x = IM_ROUND((bb_inner.Min.x + bb_inner.Max.x) * 0.5f); - RenderColorRectWithAlphaCheckerboard(window->DrawList, ImVec2(bb_inner.Min.x + grid_step, bb_inner.Min.y), bb_inner.Max, GetColorU32(col_rgb), grid_step, ImVec2(-grid_step + off, off), rounding, ImDrawCornerFlags_TopRight | ImDrawCornerFlags_BotRight); - window->DrawList->AddRectFilled(bb_inner.Min, ImVec2(mid_x, bb_inner.Max.y), GetColorU32(col_rgb_without_alpha), rounding, ImDrawCornerFlags_TopLeft | ImDrawCornerFlags_BotLeft); + RenderColorRectWithAlphaCheckerboard(window->DrawList, ImVec2(bb_inner.Min.x + grid_step, bb_inner.Min.y), bb_inner.Max, GetColorU32(col_rgb), grid_step, ImVec2(-grid_step + off, off), rounding, ImDrawFlags_RoundCornersRight); + window->DrawList->AddRectFilled(bb_inner.Min, ImVec2(mid_x, bb_inner.Max.y), GetColorU32(col_rgb_without_alpha), rounding, ImDrawFlags_RoundCornersLeft); } else { @@ -5317,7 +5504,7 @@ bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFl if (col_source.w < 1.0f) RenderColorRectWithAlphaCheckerboard(window->DrawList, bb_inner.Min, bb_inner.Max, GetColorU32(col_source), grid_step, ImVec2(off, off), rounding); else - window->DrawList->AddRectFilled(bb_inner.Min, bb_inner.Max, GetColorU32(col_source), rounding, ImDrawCornerFlags_All); + window->DrawList->AddRectFilled(bb_inner.Min, bb_inner.Max, GetColorU32(col_source), rounding); } RenderNavHighlight(bb, id); if ((flags & ImGuiColorEditFlags_NoBorder) == 0) @@ -5344,7 +5531,7 @@ bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFl // Tooltip if (!(flags & ImGuiColorEditFlags_NoTooltip) && hovered) - ColorTooltip(desc_id, &col.x, flags & (ImGuiColorEditFlags__InputMask | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)); + ColorTooltip(desc_id, &col.x, flags & (ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)); return pressed; } @@ -5353,18 +5540,18 @@ bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFl void ImGui::SetColorEditOptions(ImGuiColorEditFlags flags) { ImGuiContext& g = *GImGui; - if ((flags & ImGuiColorEditFlags__DisplayMask) == 0) - flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__DisplayMask; - if ((flags & ImGuiColorEditFlags__DataTypeMask) == 0) - flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__DataTypeMask; - if ((flags & ImGuiColorEditFlags__PickerMask) == 0) - flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__PickerMask; - if ((flags & ImGuiColorEditFlags__InputMask) == 0) - flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__InputMask; - IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags__DisplayMask)); // Check only 1 option is selected - IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags__DataTypeMask)); // Check only 1 option is selected - IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags__PickerMask)); // Check only 1 option is selected - IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags__InputMask)); // Check only 1 option is selected + if ((flags & ImGuiColorEditFlags_DisplayMask_) == 0) + flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_DisplayMask_; + if ((flags & ImGuiColorEditFlags_DataTypeMask_) == 0) + flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_DataTypeMask_; + if ((flags & ImGuiColorEditFlags_PickerMask_) == 0) + flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_PickerMask_; + if ((flags & ImGuiColorEditFlags_InputMask_) == 0) + flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_InputMask_; + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_DisplayMask_)); // Check only 1 option is selected + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_DataTypeMask_)); // Check only 1 option is selected + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_PickerMask_)); // Check only 1 option is selected + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_InputMask_)); // Check only 1 option is selected g.ColorEditOptions = flags; } @@ -5373,7 +5560,7 @@ void ImGui::ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags { ImGuiContext& g = *GImGui; - BeginTooltipEx(0, ImGuiTooltipFlags_OverridePreviousTooltip); + BeginTooltipEx(ImGuiTooltipFlags_OverridePreviousTooltip, ImGuiWindowFlags_None); const char* text_end = text ? FindRenderedTextEnd(text, NULL) : text; if (text_end > text) { @@ -5384,9 +5571,9 @@ void ImGui::ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags ImVec2 sz(g.FontSize * 3 + g.Style.FramePadding.y * 2, g.FontSize * 3 + g.Style.FramePadding.y * 2); ImVec4 cf(col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]); int cr = IM_F32_TO_INT8_SAT(col[0]), cg = IM_F32_TO_INT8_SAT(col[1]), cb = IM_F32_TO_INT8_SAT(col[2]), ca = (flags & ImGuiColorEditFlags_NoAlpha) ? 255 : IM_F32_TO_INT8_SAT(col[3]); - ColorButton("##preview", cf, (flags & (ImGuiColorEditFlags__InputMask | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)) | ImGuiColorEditFlags_NoTooltip, sz); + ColorButton("##preview", cf, (flags & (ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)) | ImGuiColorEditFlags_NoTooltip, sz); SameLine(); - if ((flags & ImGuiColorEditFlags_InputRGB) || !(flags & ImGuiColorEditFlags__InputMask)) + if ((flags & ImGuiColorEditFlags_InputRGB) || !(flags & ImGuiColorEditFlags_InputMask_)) { if (flags & ImGuiColorEditFlags_NoAlpha) Text("#%02X%02X%02X\nR: %d, G: %d, B: %d\n(%.3f, %.3f, %.3f)", cr, cg, cb, cr, cg, cb, col[0], col[1], col[2]); @@ -5405,23 +5592,23 @@ void ImGui::ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags void ImGui::ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags) { - bool allow_opt_inputs = !(flags & ImGuiColorEditFlags__DisplayMask); - bool allow_opt_datatype = !(flags & ImGuiColorEditFlags__DataTypeMask); + bool allow_opt_inputs = !(flags & ImGuiColorEditFlags_DisplayMask_); + bool allow_opt_datatype = !(flags & ImGuiColorEditFlags_DataTypeMask_); if ((!allow_opt_inputs && !allow_opt_datatype) || !BeginPopup("context")) return; ImGuiContext& g = *GImGui; ImGuiColorEditFlags opts = g.ColorEditOptions; if (allow_opt_inputs) { - if (RadioButton("RGB", (opts & ImGuiColorEditFlags_DisplayRGB) != 0)) opts = (opts & ~ImGuiColorEditFlags__DisplayMask) | ImGuiColorEditFlags_DisplayRGB; - if (RadioButton("HSV", (opts & ImGuiColorEditFlags_DisplayHSV) != 0)) opts = (opts & ~ImGuiColorEditFlags__DisplayMask) | ImGuiColorEditFlags_DisplayHSV; - if (RadioButton("Hex", (opts & ImGuiColorEditFlags_DisplayHex) != 0)) opts = (opts & ~ImGuiColorEditFlags__DisplayMask) | ImGuiColorEditFlags_DisplayHex; + if (RadioButton("RGB", (opts & ImGuiColorEditFlags_DisplayRGB) != 0)) opts = (opts & ~ImGuiColorEditFlags_DisplayMask_) | ImGuiColorEditFlags_DisplayRGB; + if (RadioButton("HSV", (opts & ImGuiColorEditFlags_DisplayHSV) != 0)) opts = (opts & ~ImGuiColorEditFlags_DisplayMask_) | ImGuiColorEditFlags_DisplayHSV; + if (RadioButton("Hex", (opts & ImGuiColorEditFlags_DisplayHex) != 0)) opts = (opts & ~ImGuiColorEditFlags_DisplayMask_) | ImGuiColorEditFlags_DisplayHex; } if (allow_opt_datatype) { if (allow_opt_inputs) Separator(); - if (RadioButton("0..255", (opts & ImGuiColorEditFlags_Uint8) != 0)) opts = (opts & ~ImGuiColorEditFlags__DataTypeMask) | ImGuiColorEditFlags_Uint8; - if (RadioButton("0.00..1.00", (opts & ImGuiColorEditFlags_Float) != 0)) opts = (opts & ~ImGuiColorEditFlags__DataTypeMask) | ImGuiColorEditFlags_Float; + if (RadioButton("0..255", (opts & ImGuiColorEditFlags_Uint8) != 0)) opts = (opts & ~ImGuiColorEditFlags_DataTypeMask_) | ImGuiColorEditFlags_Uint8; + if (RadioButton("0.00..1.00", (opts & ImGuiColorEditFlags_Float) != 0)) opts = (opts & ~ImGuiColorEditFlags_DataTypeMask_) | ImGuiColorEditFlags_Float; } if (allow_opt_inputs || allow_opt_datatype) @@ -5456,7 +5643,7 @@ void ImGui::ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags) void ImGui::ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags flags) { - bool allow_opt_picker = !(flags & ImGuiColorEditFlags__PickerMask); + bool allow_opt_picker = !(flags & ImGuiColorEditFlags_PickerMask_); bool allow_opt_alpha_bar = !(flags & ImGuiColorEditFlags_NoAlpha) && !(flags & ImGuiColorEditFlags_AlphaBar); if ((!allow_opt_picker && !allow_opt_alpha_bar) || !BeginPopup("context")) return; @@ -5475,7 +5662,7 @@ void ImGui::ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags fl if (picker_type == 1) picker_flags |= ImGuiColorEditFlags_PickerHueWheel; ImVec2 backup_pos = GetCursorScreenPos(); if (Selectable("##selectable", false, 0, picker_size)) // By default, Selectable() is closing popup - g.ColorEditOptions = (g.ColorEditOptions & ~ImGuiColorEditFlags__PickerMask) | (picker_flags & ImGuiColorEditFlags__PickerMask); + g.ColorEditOptions = (g.ColorEditOptions & ~ImGuiColorEditFlags_PickerMask_) | (picker_flags & ImGuiColorEditFlags_PickerMask_); SetCursorScreenPos(backup_pos); ImVec4 previewing_ref_col; memcpy(&previewing_ref_col, ref_col, sizeof(float) * ((picker_flags & ImGuiColorEditFlags_NoAlpha) ? 3 : 4)); @@ -5688,14 +5875,14 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l window->DC.TreeJumpToParentOnPopMask |= (1 << window->DC.TreeDepth); bool item_add = ItemAdd(interact_bb, id); - window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HasDisplayRect; - window->DC.LastItemDisplayRect = frame_bb; + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasDisplayRect; + g.LastItemData.DisplayRect = frame_bb; if (!item_add) { if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) TreePushOverrideID(id); - IMGUI_TEST_ENGINE_ITEM_INFO(window->DC.LastItemId, label, window->DC.ItemFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0)); + IMGUI_TEST_ENGINE_ITEM_INFO(g.LastItemData.ID, label, g.LastItemData.StatusFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0)); return is_open; } @@ -5744,7 +5931,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l toggled = true; if (flags & ImGuiTreeNodeFlags_OpenOnArrow) toggled |= is_mouse_x_over_arrow && !g.NavDisableMouseHover; // Lightweight equivalent of IsMouseHoveringRect() since ButtonBehavior() already did the job - if ((flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) && g.IO.MouseDoubleClicked[0]) + if ((flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) && g.IO.MouseClickedCount[0] == 2) toggled = true; } else if (pressed && g.DragDropHoldJustPressedId == id) @@ -5754,12 +5941,12 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l toggled = true; } - if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Left && is_open) + if (g.NavId == id && g.NavMoveDir == ImGuiDir_Left && is_open) { toggled = true; NavMoveRequestCancel(); } - if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Right && !is_open) // If there's something upcoming on the line we may want to give it the priority? + if (g.NavId == id && g.NavMoveDir == ImGuiDir_Right && !is_open) // If there's something upcoming on the line we may want to give it the priority? { toggled = true; NavMoveRequestCancel(); @@ -5769,7 +5956,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l { is_open = !is_open; window->DC.StateStorage->SetInt(id, is_open); - window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_ToggledOpen; + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledOpen; } } if (flags & ImGuiTreeNodeFlags_AllowItemOverlap) @@ -5777,7 +5964,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l // In this branch, TreeNodeBehavior() cannot toggle the selection so this will never trigger. if (selected != was_selected) //-V547 - window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_ToggledSelection; + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledSelection; // Render const ImU32 text_col = GetColorU32(ImGuiCol_Text); @@ -5796,19 +5983,10 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l text_pos.x -= text_offset_x; if (flags & ImGuiTreeNodeFlags_ClipLabelForTrailingButton) frame_bb.Max.x -= g.FontSize + style.FramePadding.x; + if (g.LogEnabled) - { - // NB: '##' is normally used to hide text (as a library-wide feature), so we need to specify the text range to make sure the ## aren't stripped out here. - const char log_prefix[] = "\n##"; - const char log_suffix[] = "##"; - LogRenderedText(&text_pos, log_prefix, log_prefix + 3); - RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size); - LogRenderedText(&text_pos, log_suffix, log_suffix + 2); - } - else - { - RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size); - } + LogSetNextTextDecoration("###", "###"); + RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size); } else { @@ -5817,20 +5995,20 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l { const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, false); - RenderNavHighlight(frame_bb, id, nav_highlight_flags); } + RenderNavHighlight(frame_bb, id, nav_highlight_flags); if (flags & ImGuiTreeNodeFlags_Bullet) RenderBullet(window->DrawList, ImVec2(text_pos.x - text_offset_x * 0.5f, text_pos.y + g.FontSize * 0.5f), text_col); else if (!is_leaf) RenderArrow(window->DrawList, ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y + g.FontSize * 0.15f), text_col, is_open ? ImGuiDir_Down : ImGuiDir_Right, 0.70f); if (g.LogEnabled) - LogRenderedText(&text_pos, ">"); + LogSetNextTextDecoration(">", NULL); RenderText(text_pos, label, label_end, false); } if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) TreePushOverrideID(id); - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0)); + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0)); return is_open; } @@ -5839,7 +6017,7 @@ void ImGui::TreePush(const char* str_id) ImGuiWindow* window = GetCurrentWindow(); Indent(); window->DC.TreeDepth++; - PushID(str_id ? str_id : "#TreePush"); + PushID(str_id); } void ImGui::TreePush(const void* ptr_id) @@ -5847,7 +6025,7 @@ void ImGui::TreePush(const void* ptr_id) ImGuiWindow* window = GetCurrentWindow(); Indent(); window->DC.TreeDepth++; - PushID(ptr_id ? ptr_id : (const void*)"#TreePush"); + PushID(ptr_id); } void ImGui::TreePushOverrideID(ImGuiID id) @@ -5856,7 +6034,7 @@ void ImGui::TreePushOverrideID(ImGuiID id) ImGuiWindow* window = g.CurrentWindow; Indent(); window->DC.TreeDepth++; - window->IDStack.push_back(id); + PushOverrideID(id); } void ImGui::TreePop() @@ -5872,7 +6050,7 @@ void ImGui::TreePop() if (g.NavMoveDir == ImGuiDir_Left && g.NavWindow == window && NavMoveRequestButNoResultYet()) if (g.NavIdIsAlive && (window->DC.TreeJumpToParentOnPopMask & tree_depth_mask)) { - SetNavID(window->IDStack.back(), g.NavLayer, 0); + SetNavID(window->IDStack.back(), g.NavLayer, 0, ImRect()); NavMoveRequestCancel(); } window->DC.TreeJumpToParentOnPopMask &= tree_depth_mask - 1; @@ -5934,14 +6112,14 @@ bool ImGui::CollapsingHeader(const char* label, bool* p_visible, ImGuiTreeNodeFl // FIXME: We can evolve this into user accessible helpers to add extra buttons on title bars, headers, etc. // FIXME: CloseButton can overlap into text, need find a way to clip the text somehow. ImGuiContext& g = *GImGui; - ImGuiLastItemDataBackup last_item_backup; + ImGuiLastItemData last_item_backup = g.LastItemData; float button_size = g.FontSize; - float button_x = ImMax(window->DC.LastItemRect.Min.x, window->DC.LastItemRect.Max.x - g.Style.FramePadding.x * 2.0f - button_size); - float button_y = window->DC.LastItemRect.Min.y; + float button_x = ImMax(g.LastItemData.Rect.Min.x, g.LastItemData.Rect.Max.x - g.Style.FramePadding.x * 2.0f - button_size); + float button_y = g.LastItemData.Rect.Min.y; ImGuiID close_button_id = GetIDWithSeed("#CLOSE", NULL, id); if (CloseButton(close_button_id, ImVec2(button_x, button_y))) *p_visible = false; - last_item_backup.Restore(); + g.LastItemData = last_item_backup; } return is_open; @@ -5975,7 +6153,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl ItemSize(size, 0.0f); // Fill horizontal space - // We don't support (size < 0.0f) in Selectable() because the ItemSpacing extension would make explicitely right-aligned sizes not visibly match other widgets. + // We don't support (size < 0.0f) in Selectable() because the ItemSpacing extension would make explicitly right-aligned sizes not visibly match other widgets. const bool span_all_columns = (flags & ImGuiSelectableFlags_SpanAllColumns) != 0; const float min_x = span_all_columns ? window->ParentWorkRect.Min.x : pos.x; const float max_x = span_all_columns ? window->ParentWorkRect.Max.x : window->WorkRect.Max.x; @@ -6010,19 +6188,8 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl window->ClipRect.Max.x = window->ParentWorkRect.Max.x; } - bool item_add; - if (flags & ImGuiSelectableFlags_Disabled) - { - ImGuiItemFlags backup_item_flags = window->DC.ItemFlags; - window->DC.ItemFlags |= ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNavDefaultFocus; - item_add = ItemAdd(bb, id); - window->DC.ItemFlags = backup_item_flags; - } - else - { - item_add = ItemAdd(bb, id); - } - + const bool disabled_item = (flags & ImGuiSelectableFlags_Disabled) != 0; + const bool item_add = ItemAdd(bb, id, NULL, disabled_item ? ImGuiItemFlags_Disabled : ImGuiItemFlags_None); if (span_all_columns) { window->ClipRect.Min.x = backup_clip_rect_min_x; @@ -6032,6 +6199,10 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl if (!item_add) return false; + const bool disabled_global = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0; + if (disabled_item && !disabled_global) // Only testing this as an optimization + BeginDisabled(); + // FIXME: We can standardize the behavior of those two, we could also keep the fast path of override ClipRect + full push on render only, // which would be advantageous since most selectable are not selected. if (span_all_columns && window->DC.CurrentColumns) @@ -6044,24 +6215,31 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl if (flags & ImGuiSelectableFlags_NoHoldingActiveID) { button_flags |= ImGuiButtonFlags_NoHoldingActiveId; } if (flags & ImGuiSelectableFlags_SelectOnClick) { button_flags |= ImGuiButtonFlags_PressedOnClick; } if (flags & ImGuiSelectableFlags_SelectOnRelease) { button_flags |= ImGuiButtonFlags_PressedOnRelease; } - if (flags & ImGuiSelectableFlags_Disabled) { button_flags |= ImGuiButtonFlags_Disabled; } if (flags & ImGuiSelectableFlags_AllowDoubleClick) { button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick; } if (flags & ImGuiSelectableFlags_AllowItemOverlap) { button_flags |= ImGuiButtonFlags_AllowItemOverlap; } - if (flags & ImGuiSelectableFlags_Disabled) - selected = false; - const bool was_selected = selected; bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags); + // Auto-select when moved into + // - This will be more fully fleshed in the range-select branch + // - This is not exposed as it won't nicely work with some user side handling of shift/control + // - We cannot do 'if (g.NavJustMovedToId != id) { selected = false; pressed = was_selected; }' for two reasons + // - (1) it would require focus scope to be set, need exposing PushFocusScope() or equivalent (e.g. BeginSelection() calling PushFocusScope()) + // - (2) usage will fail with clipped items + // The multi-select API aim to fix those issues, e.g. may be replaced with a BeginSelection() API. + if ((flags & ImGuiSelectableFlags_SelectOnNav) && g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == window->DC.NavFocusScopeIdCurrent) + if (g.NavJustMovedToId == id) + selected = pressed = true; + // Update NavId when clicking or when Hovering (this doesn't happen on most widgets), so navigation can be resumed with gamepad/keyboard if (pressed || (hovered && (flags & ImGuiSelectableFlags_SetNavIdOnHover))) { if (!g.NavDisableMouseHover && g.NavWindow == window && g.NavLayer == window->DC.NavLayerCurrent) { + SetNavID(id, window->DC.NavLayerCurrent, window->DC.NavFocusScopeIdCurrent, WindowRectAbsToRel(window, bb)); // (bb == NavRect) g.NavDisableHighlight = true; - SetNavID(id, window->DC.NavLayerCurrent, window->DC.NavFocusScopeIdCurrent); } } if (pressed) @@ -6072,7 +6250,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl // In this branch, Selectable() cannot toggle the selection so this will never trigger. if (selected != was_selected) //-V547 - window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_ToggledSelection; + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledSelection; // Render if (held && (flags & ImGuiSelectableFlags_DrawHoveredWhenHeld)) @@ -6081,24 +6259,25 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl { const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); RenderFrame(bb.Min, bb.Max, col, false, 0.0f); - RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding); } + RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding); if (span_all_columns && window->DC.CurrentColumns) PopColumnsBackground(); else if (span_all_columns && g.CurrentTable) TablePopBackgroundChannel(); - if (flags & ImGuiSelectableFlags_Disabled) PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_TextDisabled]); RenderTextClipped(text_min, text_max, label, NULL, &label_size, style.SelectableTextAlign, &bb); - if (flags & ImGuiSelectableFlags_Disabled) PopStyleColor(); // Automatically close popups - if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_DontClosePopups) && !(window->DC.ItemFlags & ImGuiItemFlags_SelectableDontClosePopup)) + if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_DontClosePopups) && !(g.LastItemData.InFlags & ImGuiItemFlags_SelectableDontClosePopup)) CloseCurrentPopup(); - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags); - return pressed; + if (disabled_item && !disabled_global) + EndDisabled(); + + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); + return pressed; //-V1020 } bool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags, const ImVec2& size_arg) @@ -6114,18 +6293,14 @@ bool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags //------------------------------------------------------------------------- // [SECTION] Widgets: ListBox //------------------------------------------------------------------------- +// - BeginListBox() +// - EndListBox() // - ListBox() -// - ListBoxHeader() -// - ListBoxFooter() -//------------------------------------------------------------------------- -// FIXME: This is an old API. We should redesign some of it, rename ListBoxHeader->BeginListBox, ListBoxFooter->EndListBox -// and promote using them over existing ListBox() functions, similarly to change with combo boxes. //------------------------------------------------------------------------- -// FIXME: In principle this function should be called BeginListBox(). We should rename it after re-evaluating if we want to keep the same signature. -// Helper to calculate the size of a listbox and display a label on the right. -// Tip: To have a list filling the entire window width, PushItemWidth(-1) and pass an non-visible label e.g. "##empty" -bool ImGui::ListBoxHeader(const char* label, const ImVec2& size_arg) +// Tip: To have a list filling the entire window width, use size.x = -FLT_MIN and pass an non-visible label e.g. "##empty" +// Tip: If your vertical size is calculated from an item count (e.g. 10 * item_height) consider adding a fractional part to facilitate seeing scrolling boundaries (e.g. 10.25 * item_height). +bool ImGui::BeginListBox(const char* label, const ImVec2& size_arg) { ImGuiContext& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); @@ -6136,12 +6311,12 @@ bool ImGui::ListBoxHeader(const char* label, const ImVec2& size_arg) const ImGuiID id = GetID(label); const ImVec2 label_size = CalcTextSize(label, NULL, true); - // Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar. - ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), GetTextLineHeightWithSpacing() * 7.4f + style.ItemSpacing.y); + // Size default to hold ~7.25 items. + // Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar. + ImVec2 size = ImFloor(CalcItemSize(size_arg, CalcItemWidth(), GetTextLineHeightWithSpacing() * 7.25f + style.FramePadding.y * 2.0f)); ImVec2 frame_size = ImVec2(size.x, ImMax(size.y, label_size.y)); ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size); ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); - window->DC.LastItemRect = bb; // Forward storage for ListBoxFooter.. dodgy. g.NextItemData.ClearFlags(); if (!IsRectVisible(bb.Min, bb.Max)) @@ -6151,50 +6326,42 @@ bool ImGui::ListBoxHeader(const char* label, const ImVec2& size_arg) return false; } + // FIXME-OPT: We could omit the BeginGroup() if label_size.x but would need to omit the EndGroup() as well. BeginGroup(); - if (label_size.x > 0) - RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); + if (label_size.x > 0.0f) + { + ImVec2 label_pos = ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y); + RenderText(label_pos, label); + window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, label_pos + label_size); + } BeginChildFrame(id, frame_bb.GetSize()); return true; } -// FIXME: In principle this function should be called EndListBox(). We should rename it after re-evaluating if we want to keep the same signature. +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS +// OBSOLETED in 1.81 (from February 2021) bool ImGui::ListBoxHeader(const char* label, int items_count, int height_in_items) { - // Size default to hold ~7.25 items. - // We add +25% worth of item height to allow the user to see at a glance if there are more items up/down, without looking at the scrollbar. - // We don't add this extra bit if items_count <= height_in_items. It is slightly dodgy, because it means a dynamic list of items will make the widget resize occasionally when it crosses that size. - // I am expecting that someone will come and complain about this behavior in a remote future, then we can advise on a better solution. - if (height_in_items < 0) - height_in_items = ImMin(items_count, 7); - const ImGuiStyle& style = GetStyle(); - float height_in_items_f = (height_in_items < items_count) ? (height_in_items + 0.25f) : (height_in_items + 0.00f); - - // We include ItemSpacing.y so that a list sized for the exact number of items doesn't make a scrollbar appears. We could also enforce that by passing a flag to BeginChild(). + // If height_in_items == -1, default height is maximum 7. + ImGuiContext& g = *GImGui; + float height_in_items_f = (height_in_items < 0 ? ImMin(items_count, 7) : height_in_items) + 0.25f; ImVec2 size; size.x = 0.0f; - size.y = ImFloor(GetTextLineHeightWithSpacing() * height_in_items_f + style.FramePadding.y * 2.0f); - return ListBoxHeader(label, size); + size.y = GetTextLineHeightWithSpacing() * height_in_items_f + g.Style.FramePadding.y * 2.0f; + return BeginListBox(label, size); } +#endif -// FIXME: In principle this function should be called EndListBox(). We should rename it after re-evaluating if we want to keep the same signature. -void ImGui::ListBoxFooter() +void ImGui::EndListBox() { - ImGuiWindow * window = GetCurrentWindow(); - IM_ASSERT((window->Flags & ImGuiWindowFlags_ChildWindow) && "Mismatched ListBoxHeader/ListBoxFooter calls. Did you test the return value of ListBoxHeader()?"); - ImGuiWindow* parent_window = window->ParentWindow; - const ImRect bb = parent_window->DC.LastItemRect; - const ImGuiStyle& style = GetStyle(); + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + IM_ASSERT((window->Flags & ImGuiWindowFlags_ChildWindow) && "Mismatched BeginListBox/EndListBox calls. Did you test the return value of BeginListBox?"); + IM_UNUSED(window); EndChildFrame(); - - // Redeclare item size so that it includes the label (we have stored the full size in LastItemRect) - // We call SameLine() to restore DC.CurrentLine* data - SameLine(); - parent_window->DC.CursorPos = bb.Min; - ItemSize(bb, style.FramePadding.y); - EndGroup(); + EndGroup(); // This is only required to be able to do IsItemXXX query on the whole ListBox including label } bool ImGui::ListBox(const char* label, int* current_item, const char* const items[], int items_count, int height_items) @@ -6203,25 +6370,35 @@ bool ImGui::ListBox(const char* label, int* current_item, const char* const item return value_changed; } +// This is merely a helper around BeginListBox(), EndListBox(). +// Considering using those directly to submit custom data or store selection differently. bool ImGui::ListBox(const char* label, int* current_item, bool (*items_getter)(void*, int, const char**), void* data, int items_count, int height_in_items) { - if (!ListBoxHeader(label, items_count, height_in_items)) + ImGuiContext& g = *GImGui; + + // Calculate size from "height_in_items" + if (height_in_items < 0) + height_in_items = ImMin(items_count, 7); + float height_in_items_f = height_in_items + 0.25f; + ImVec2 size(0.0f, ImFloor(GetTextLineHeightWithSpacing() * height_in_items_f + g.Style.FramePadding.y * 2.0f)); + + if (!BeginListBox(label, size)) return false; - // Assume all items have even height (= 1 line of text). If you need items of different or variable sizes you can create a custom version of ListBox() in your code without using the clipper. - ImGuiContext& g = *GImGui; + // Assume all items have even height (= 1 line of text). If you need items of different height, + // you can create a custom version of ListBox() in your code without using the clipper. bool value_changed = false; ImGuiListClipper clipper; clipper.Begin(items_count, GetTextLineHeightWithSpacing()); // We know exactly our line height here so we pass it as a minor optimization, but generally you don't need to. while (clipper.Step()) for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) { - const bool item_selected = (i == *current_item); const char* item_text; if (!items_getter(data, i, &item_text)) item_text = "*Unknown item*"; PushID(i); + const bool item_selected = (i == *current_item); if (Selectable(item_text, item_selected)) { *current_item = i; @@ -6231,9 +6408,10 @@ bool ImGui::ListBox(const char* label, int* current_item, bool (*items_getter)(v SetItemDefaultFocus(); PopID(); } - ListBoxFooter(); + EndListBox(); + if (value_changed) - MarkItemEdited(g.CurrentWindow->DC.LastItemId); + MarkItemEdited(g.LastItemData.ID); return value_changed; } @@ -6248,7 +6426,7 @@ bool ImGui::ListBox(const char* label, int* current_item, bool (*items_getter)(v // Plot/Graph widgets are not very good. // Consider writing your own, or using a third-party one, see: // - ImPlot https://github.com/epezent/implot -// - others https://github.com/ocornut/imgui/wiki/Useful-Widgets +// - others https://github.com/ocornut/imgui/wiki/Useful-Extensions //------------------------------------------------------------------------- int ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 frame_size) @@ -6325,7 +6503,7 @@ int ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_get float v0 = values_getter(data, (0 + values_offset) % values_count); float t0 = 0.0f; ImVec2 tp0 = ImVec2( t0, 1.0f - ImSaturate((v0 - scale_min) * inv_scale) ); // Point in the normalized space of our target rectangle - float histogram_zero_line_t = (scale_min * scale_max < 0.0f) ? (-scale_min * inv_scale) : (scale_min < 0.0f ? 0.0f : 1.0f); // Where does the zero line stands + float histogram_zero_line_t = (scale_min * scale_max < 0.0f) ? (1 + scale_min * inv_scale) : (scale_min < 0.0f ? 0.0f : 1.0f); // Where does the zero line stands const ImU32 col_base = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLines : ImGuiCol_PlotHistogram); const ImU32 col_hovered = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLinesHovered : ImGuiCol_PlotHistogramHovered); @@ -6452,42 +6630,51 @@ void ImGui::Value(const char* prefix, float v, const char* float_format) // - EndMainMenuBar() // - BeginMenu() // - EndMenu() +// - MenuItemEx() [Internal] // - MenuItem() //------------------------------------------------------------------------- // Helpers for internal use -void ImGuiMenuColumns::Update(int count, float spacing, bool clear) +void ImGuiMenuColumns::Update(float spacing, bool window_reappearing) { - IM_ASSERT(count == IM_ARRAYSIZE(Pos)); - IM_UNUSED(count); - Width = NextWidth = 0.0f; - Spacing = spacing; - if (clear) - memset(NextWidths, 0, sizeof(NextWidths)); - for (int i = 0; i < IM_ARRAYSIZE(Pos); i++) - { - if (i > 0 && NextWidths[i] > 0.0f) - Width += Spacing; - Pos[i] = IM_FLOOR(Width); - Width += NextWidths[i]; - NextWidths[i] = 0.0f; - } + if (window_reappearing) + memset(Widths, 0, sizeof(Widths)); + Spacing = (ImU16)spacing; + CalcNextTotalWidth(true); + memset(Widths, 0, sizeof(Widths)); + TotalWidth = NextTotalWidth; + NextTotalWidth = 0; } -float ImGuiMenuColumns::DeclColumns(float w0, float w1, float w2) // not using va_arg because they promote float to double +void ImGuiMenuColumns::CalcNextTotalWidth(bool update_offsets) { - NextWidth = 0.0f; - NextWidths[0] = ImMax(NextWidths[0], w0); - NextWidths[1] = ImMax(NextWidths[1], w1); - NextWidths[2] = ImMax(NextWidths[2], w2); - for (int i = 0; i < IM_ARRAYSIZE(Pos); i++) - NextWidth += NextWidths[i] + ((i > 0 && NextWidths[i] > 0.0f) ? Spacing : 0.0f); - return ImMax(Width, NextWidth); + ImU16 offset = 0; + bool want_spacing = false; + for (int i = 0; i < IM_ARRAYSIZE(Widths); i++) + { + ImU16 width = Widths[i]; + if (want_spacing && width > 0) + offset += Spacing; + want_spacing |= (width > 0); + if (update_offsets) + { + if (i == 1) { OffsetLabel = offset; } + if (i == 2) { OffsetShortcut = offset; } + if (i == 3) { OffsetMark = offset; } + } + offset += width; + } + NextTotalWidth = offset; } -float ImGuiMenuColumns::CalcExtraSpace(float avail_w) const +float ImGuiMenuColumns::DeclColumns(float w_icon, float w_label, float w_shortcut, float w_mark) { - return ImMax(0.0f, avail_w - Width); + Widths[0] = ImMax(Widths[0], (ImU16)w_icon); + Widths[1] = ImMax(Widths[1], (ImU16)w_label); + Widths[2] = ImMax(Widths[2], (ImU16)w_shortcut); + Widths[3] = ImMax(Widths[3], (ImU16)w_mark); + CalcNextTotalWidth(false); + return (float)ImMax(TotalWidth, NextTotalWidth); } // FIXME: Provided a rectangle perhaps e.g. a BeginMenuBarEx() could be used anywhere.. @@ -6513,7 +6700,7 @@ bool ImGui::BeginMenuBar() clip_rect.ClipWith(window->OuterRectClipped); PushClipRect(clip_rect.Min, clip_rect.Max, false); - // We overwrite CursorMaxPos because BeginGroup sets it to CursorPos (essentially the .EmitItem hack in EndMenuBar() would need something analoguous here, maybe a BeginGroupEx() with flags). + // We overwrite CursorMaxPos because BeginGroup sets it to CursorPos (essentially the .EmitItem hack in EndMenuBar() would need something analogous here, maybe a BeginGroupEx() with flags). window->DC.CursorPos = window->DC.CursorMaxPos = ImVec2(bar_rect.Min.x + window->DC.MenuBarOffset.x, bar_rect.Min.y + window->DC.MenuBarOffset.y); window->DC.LayoutType = ImGuiLayoutType_Horizontal; window->DC.NavLayerCurrent = ImGuiNavLayer_Menu; @@ -6532,29 +6719,30 @@ void ImGui::EndMenuBar() // Nav: When a move request within one of our child menu failed, capture the request to navigate among our siblings. if (NavMoveRequestButNoResultYet() && (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) && (g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu)) { + // Try to find out if the request is for one of our child menu ImGuiWindow* nav_earliest_child = g.NavWindow; while (nav_earliest_child->ParentWindow && (nav_earliest_child->ParentWindow->Flags & ImGuiWindowFlags_ChildMenu)) nav_earliest_child = nav_earliest_child->ParentWindow; - if (nav_earliest_child->ParentWindow == window && nav_earliest_child->DC.ParentLayoutType == ImGuiLayoutType_Horizontal && g.NavMoveRequestForward == ImGuiNavForward_None) + if (nav_earliest_child->ParentWindow == window && nav_earliest_child->DC.ParentLayoutType == ImGuiLayoutType_Horizontal && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded) == 0) { // To do so we claim focus back, restore NavId and then process the movement request for yet another frame. - // This involve a one-frame delay which isn't very problematic in this situation. We could remove it by scoring in advance for multiple window (probably not worth the hassle/cost) + // This involve a one-frame delay which isn't very problematic in this situation. We could remove it by scoring in advance for multiple window (probably not worth bothering) const ImGuiNavLayer layer = ImGuiNavLayer_Menu; - IM_ASSERT(window->DC.NavLayerActiveMaskNext & (1 << layer)); // Sanity check + IM_ASSERT(window->DC.NavLayersActiveMaskNext & (1 << layer)); // Sanity check FocusWindow(window); - SetNavIDWithRectRel(window->NavLastIds[layer], layer, 0, window->NavRectRel[layer]); - g.NavLayer = layer; + SetNavID(window->NavLastIds[layer], layer, 0, window->NavRectRel[layer]); g.NavDisableHighlight = true; // Hide highlight for the current frame so we don't see the intermediary selection. - g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued; - NavMoveRequestCancel(); + g.NavDisableMouseHover = g.NavMousePosDirty = true; + NavMoveRequestForward(g.NavMoveDir, g.NavMoveClipDir, g.NavMoveFlags, g.NavMoveScrollFlags); // Repeat } } + IM_MSVC_WARNING_SUPPRESS(6011); // Static Analysis false positive "warning C6011: Dereferencing NULL pointer 'window'" IM_ASSERT(window->Flags & ImGuiWindowFlags_MenuBar); IM_ASSERT(window->DC.MenuBarAppending); PopClipRect(); PopID(); - window->DC.MenuBarOffset.x = window->DC.CursorPos.x - window->MenuBarRect().Min.x; // Save horizontal position so next append can reuse it. This is kinda equivalent to a per-layer CursorPos. + window->DC.MenuBarOffset.x = window->DC.CursorPos.x - window->Pos.x; // Save horizontal position so next append can reuse it. This is kinda equivalent to a per-layer CursorPos. g.GroupStack.back().EmitItem = false; EndGroup(); // Restore position on layer 0 window->DC.LayoutType = ImGuiLayoutType_Vertical; @@ -6562,25 +6750,63 @@ void ImGui::EndMenuBar() window->DC.MenuBarAppending = false; } -// For the main menu bar, which cannot be moved, we honor g.Style.DisplaySafeAreaPadding to ensure text can be visible on a TV set. +// Important: calling order matters! +// FIXME: Somehow overlapping with docking tech. +// FIXME: The "rect-cut" aspect of this could be formalized into a lower-level helper (rect-cut: https://halt.software/dead-simple-layouts) +bool ImGui::BeginViewportSideBar(const char* name, ImGuiViewport* viewport_p, ImGuiDir dir, float axis_size, ImGuiWindowFlags window_flags) +{ + IM_ASSERT(dir != ImGuiDir_None); + + ImGuiWindow* bar_window = FindWindowByName(name); + if (bar_window == NULL || bar_window->BeginCount == 0) + { + // Calculate and set window size/position + ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)(viewport_p ? viewport_p : GetMainViewport()); + ImRect avail_rect = viewport->GetBuildWorkRect(); + ImGuiAxis axis = (dir == ImGuiDir_Up || dir == ImGuiDir_Down) ? ImGuiAxis_Y : ImGuiAxis_X; + ImVec2 pos = avail_rect.Min; + if (dir == ImGuiDir_Right || dir == ImGuiDir_Down) + pos[axis] = avail_rect.Max[axis] - axis_size; + ImVec2 size = avail_rect.GetSize(); + size[axis] = axis_size; + SetNextWindowPos(pos); + SetNextWindowSize(size); + + // Report our size into work area (for next frame) using actual window size + if (dir == ImGuiDir_Up || dir == ImGuiDir_Left) + viewport->BuildWorkOffsetMin[axis] += axis_size; + else if (dir == ImGuiDir_Down || dir == ImGuiDir_Right) + viewport->BuildWorkOffsetMax[axis] -= axis_size; + } + + window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove; + PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0, 0)); // Lift normal size constraint + bool is_open = Begin(name, NULL, window_flags); + PopStyleVar(2); + + return is_open; +} + bool ImGui::BeginMainMenuBar() { ImGuiContext& g = *GImGui; + ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)GetMainViewport(); + + // For the main menu bar, which cannot be moved, we honor g.Style.DisplaySafeAreaPadding to ensure text can be visible on a TV set. + // FIXME: This could be generalized as an opt-in way to clamp window->DC.CursorStartPos to avoid SafeArea? + // FIXME: Consider removing support for safe area down the line... it's messy. Nowadays consoles have support for TV calibration in OS settings. g.NextWindowData.MenuBarOffsetMinVal = ImVec2(g.Style.DisplaySafeAreaPadding.x, ImMax(g.Style.DisplaySafeAreaPadding.y - g.Style.FramePadding.y, 0.0f)); - SetNextWindowPos(ImVec2(0.0f, 0.0f)); - SetNextWindowSize(ImVec2(g.IO.DisplaySize.x, g.NextWindowData.MenuBarOffsetMinVal.y + g.FontBaseSize + g.Style.FramePadding.y)); - PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); - PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0, 0)); - ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_MenuBar; - bool is_open = Begin("##MainMenuBar", NULL, window_flags) && BeginMenuBar(); - PopStyleVar(2); + ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_MenuBar; + float height = GetFrameHeight(); + bool is_open = BeginViewportSideBar("##MainMenuBar", viewport, ImGuiDir_Up, height, window_flags); g.NextWindowData.MenuBarOffsetMinVal = ImVec2(0.0f, 0.0f); - if (!is_open) - { + + if (is_open) + BeginMenuBar(); + else End(); - return false; - } - return true; //-V1020 + return is_open; } void ImGui::EndMainMenuBar() @@ -6596,7 +6822,24 @@ void ImGui::EndMainMenuBar() End(); } -bool ImGui::BeginMenu(const char* label, bool enabled) +static bool IsRootOfOpenMenuSet() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + if ((g.OpenPopupStack.Size <= g.BeginPopupStack.Size) || (window->Flags & ImGuiWindowFlags_ChildMenu)) + return false; + + // Initially we used 'OpenParentId' to differentiate multiple menu sets from each others (e.g. inside menu bar vs loose menu items) based on parent ID. + // This would however prevent the use of e.g. PuhsID() user code submitting menus. + // Previously this worked between popup and a first child menu because the first child menu always had the _ChildWindow flag, + // making hovering on parent popup possible while first child menu was focused - but this was generally a bug with other side effects. + // Instead we don't treat Popup specifically (in order to consistently support menu features in them), maybe the first child menu of a Popup + // doesn't have the _ChildWindow flag, and we rely on this IsRootOfOpenMenuSet() check to allow hovering between root window/popup and first chilld menu. + const ImGuiPopupData* upper_popup = &g.OpenPopupStack[g.BeginPopupStack.Size]; + return (/*upper_popup->OpenParentId == window->IDStack.back() &&*/ upper_popup->Window && (upper_popup->Window->Flags & ImGuiWindowFlags_ChildMenu)); +} + +bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) @@ -6608,8 +6851,9 @@ bool ImGui::BeginMenu(const char* label, bool enabled) bool menu_is_open = IsPopupOpen(id, ImGuiPopupFlags_None); // Sub-menus are ChildWindow so that mouse can be hovering across them (otherwise top-most popup menu would steal focus and not allow hovering on parent menu) + // The first menu in a hierarchy isn't so hovering doesn't get accross (otherwise e.g. resizing borders with ImGuiButtonFlags_FlattenChildren would react), but top-most BeginMenu() will bypass that limitation. ImGuiWindowFlags flags = ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus; - if (window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) + if (window->Flags & ImGuiWindowFlags_ChildMenu) flags |= ImGuiWindowFlags_ChildWindow; // If a menu with same the ID was already submitted, we will append to it, matching the behavior of Begin(). @@ -6628,16 +6872,23 @@ bool ImGui::BeginMenu(const char* label, bool enabled) g.MenusIdSubmittedThisFrame.push_back(id); ImVec2 label_size = CalcTextSize(label, NULL, true); - bool pressed; - bool menuset_is_open = !(window->Flags & ImGuiWindowFlags_Popup) && (g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].OpenParentId == window->IDStack.back()); + + // Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent without always being a Child window) + const bool menuset_is_open = IsRootOfOpenMenuSet(); ImGuiWindow* backed_nav_window = g.NavWindow; if (menuset_is_open) - g.NavWindow = window; // Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent) + g.NavWindow = window; // The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu, // However the final position is going to be different! It is chosen by FindBestWindowPosForPopup(). // e.g. Menus tend to overlap each other horizontally to amplify relative Z-ordering. ImVec2 popup_pos, pos = window->DC.CursorPos; + PushID(label); + if (!enabled) + BeginDisabled(); + const ImGuiMenuColumns* offsets = &window->DC.MenuColumns; + bool pressed; + const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_DontClosePopups; if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) { // Menu inside an horizontal menu bar @@ -6647,24 +6898,33 @@ bool ImGui::BeginMenu(const char* label, bool enabled) window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * 0.5f); PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x * 2.0f, style.ItemSpacing.y)); float w = label_size.x; - pressed = Selectable(label, menu_is_open, ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_DontClosePopups | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f)); + ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset); + pressed = Selectable("", menu_is_open, selectable_flags, ImVec2(w, 0.0f)); + RenderText(text_pos, label); PopStyleVar(); window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar(). } else { - // Menu inside a menu + // Menu inside a regular/vertical menu // (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f. // Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system. popup_pos = ImVec2(pos.x, pos.y - style.WindowPadding.y); - float min_w = window->DC.MenuColumns.DeclColumns(label_size.x, 0.0f, IM_FLOOR(g.FontSize * 1.20f)); // Feedback to next frame + float icon_w = (icon && icon[0]) ? CalcTextSize(icon, NULL).x : 0.0f; + float checkmark_w = IM_FLOOR(g.FontSize * 1.20f); + float min_w = window->DC.MenuColumns.DeclColumns(icon_w, label_size.x, 0.0f, checkmark_w); // Feedback to next frame float extra_w = ImMax(0.0f, GetContentRegionAvail().x - min_w); - pressed = Selectable(label, menu_is_open, ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_DontClosePopups | ImGuiSelectableFlags_SpanAvailWidth | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(min_w, 0.0f)); - ImU32 text_col = GetColorU32(enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled); - RenderArrow(window->DrawList, pos + ImVec2(window->DC.MenuColumns.Pos[2] + extra_w + g.FontSize * 0.30f, 0.0f), text_col, ImGuiDir_Right); + ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset); + pressed = Selectable("", menu_is_open, selectable_flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, 0.0f)); + RenderText(text_pos, label); + if (icon_w > 0.0f) + RenderText(pos + ImVec2(offsets->OffsetIcon, 0.0f), icon); + RenderArrow(window->DrawList, pos + ImVec2(offsets->OffsetMark + extra_w + g.FontSize * 0.30f, 0.0f), GetColorU32(ImGuiCol_Text), ImGuiDir_Right); } + if (!enabled) + EndDisabled(); - const bool hovered = enabled && ItemHoverable(window->DC.LastItemRect, id); + const bool hovered = (g.HoveredId == id) && enabled; if (menuset_is_open) g.NavWindow = backed_nav_window; @@ -6675,36 +6935,30 @@ bool ImGui::BeginMenu(const char* label, bool enabled) // Close menu when not hovering it anymore unless we are moving roughly in the direction of the menu // Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive. bool moving_toward_other_child_menu = false; - ImGuiWindow* child_menu_window = (g.BeginPopupStack.Size < g.OpenPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].SourceWindow == window) ? g.OpenPopupStack[g.BeginPopupStack.Size].Window : NULL; if (g.HoveredWindow == window && child_menu_window != NULL && !(window->Flags & ImGuiWindowFlags_MenuBar)) { - // FIXME-DPI: Values should be derived from a master "scale" factor. + float ref_unit = g.FontSize; // FIXME-DPI ImRect next_window_rect = child_menu_window->Rect(); - ImVec2 ta = g.IO.MousePos - g.IO.MouseDelta; + ImVec2 ta = (g.IO.MousePos - g.IO.MouseDelta); ImVec2 tb = (window->Pos.x < child_menu_window->Pos.x) ? next_window_rect.GetTL() : next_window_rect.GetTR(); ImVec2 tc = (window->Pos.x < child_menu_window->Pos.x) ? next_window_rect.GetBL() : next_window_rect.GetBR(); - float extra = ImClamp(ImFabs(ta.x - tb.x) * 0.30f, 5.0f, 30.0f); // add a bit of extra slack. - ta.x += (window->Pos.x < child_menu_window->Pos.x) ? -0.5f : +0.5f; // to avoid numerical issues - tb.y = ta.y + ImMax((tb.y - extra) - ta.y, -100.0f); // triangle is maximum 200 high to limit the slope and the bias toward large sub-menus // FIXME: Multiply by fb_scale? - tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +100.0f); + float extra = ImClamp(ImFabs(ta.x - tb.x) * 0.30f, ref_unit * 0.5f, ref_unit * 2.5f); // add a bit of extra slack. + ta.x += (window->Pos.x < child_menu_window->Pos.x) ? -0.5f : +0.5f; // to avoid numerical issues (FIXME: ??) + tb.y = ta.y + ImMax((tb.y - extra) - ta.y, -ref_unit * 8.0f); // triangle is maximum 200 high to limit the slope and the bias toward large sub-menus // FIXME: Multiply by fb_scale? + tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +ref_unit * 8.0f); moving_toward_other_child_menu = ImTriangleContainsPoint(ta, tb, tc, g.IO.MousePos); - //GetForegroundDrawList()->AddTriangleFilled(ta, tb, tc, moving_within_opened_triangle ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); // [DEBUG] + //GetForegroundDrawList()->AddTriangleFilled(ta, tb, tc, moving_toward_other_child_menu ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); // [DEBUG] } if (menu_is_open && !hovered && g.HoveredWindow == window && g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrame != id && !moving_toward_other_child_menu) want_close = true; - if (!menu_is_open && hovered && pressed) // Click to open + // Open + if (!menu_is_open && pressed) // Click/activate to open want_open = true; else if (!menu_is_open && hovered && !moving_toward_other_child_menu) // Hover to open want_open = true; - - if (g.NavActivateId == id) - { - want_close = menu_is_open; - want_open = !menu_is_open; - } - if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Right) // Nav-Right to open + if (g.NavId == id && g.NavMoveDir == ImGuiDir_Right) // Nav-Right to open { want_open = true; NavMoveRequestCancel(); @@ -6722,7 +6976,7 @@ bool ImGui::BeginMenu(const char* label, bool enabled) { want_open = true; } - else if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Down) // Nav-Down to open + else if (g.NavId == id && g.NavMoveDir == ImGuiDir_Down) // Nav-Down to open { want_open = true; NavMoveRequestCancel(); @@ -6734,7 +6988,8 @@ bool ImGui::BeginMenu(const char* label, bool enabled) if (want_close && IsPopupOpen(id, ImGuiPopupFlags_None)) ClosePopupToLevel(g.BeginPopupStack.Size, true); - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags | ImGuiItemStatusFlags_Openable | (menu_is_open ? ImGuiItemStatusFlags_Opened : 0)); + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Openable | (menu_is_open ? ImGuiItemStatusFlags_Opened : 0)); + PopID(); if (!menu_is_open && want_open && g.OpenPopupStack.Size > g.BeginPopupStack.Size) { @@ -6749,8 +7004,10 @@ bool ImGui::BeginMenu(const char* label, bool enabled) if (menu_is_open) { - SetNextWindowPos(popup_pos, ImGuiCond_Always); + SetNextWindowPos(popup_pos, ImGuiCond_Always); // Note: this is super misleading! The value will serve as reference for FindBestWindowPosForPopup(), not actual pos. + PushStyleVar(ImGuiStyleVar_ChildRounding, style.PopupRounding); // First level will use _PopupRounding, subsequent will use _ChildRounding menu_is_open = BeginPopupEx(id, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display) + PopStyleVar(); } else { @@ -6760,6 +7017,11 @@ bool ImGui::BeginMenu(const char* label, bool enabled) return menu_is_open; } +bool ImGui::BeginMenu(const char* label, bool enabled) +{ + return BeginMenuEx(label, NULL, enabled); +} + void ImGui::EndMenu() { // Nav: When a left move request _within our child menu_ failed, close ourselves (the _parent_ menu). @@ -6767,16 +7029,17 @@ void ImGui::EndMenu() // However, it means that with the current code, a BeginMenu() from outside another menu or a menu-bar won't be closable with the Left direction. ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - if (g.NavWindow && g.NavWindow->ParentWindow == window && g.NavMoveDir == ImGuiDir_Left && NavMoveRequestButNoResultYet() && window->DC.LayoutType == ImGuiLayoutType_Vertical) - { - ClosePopupToLevel(g.BeginPopupStack.Size, true); - NavMoveRequestCancel(); - } + if (g.NavMoveDir == ImGuiDir_Left && NavMoveRequestButNoResultYet() && window->DC.LayoutType == ImGuiLayoutType_Vertical) + if (g.NavWindow && (g.NavWindow->RootWindowForNav->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->RootWindowForNav->ParentWindow == window) + { + ClosePopupToLevel(g.BeginPopupStack.Size, true); + NavMoveRequestCancel(); + } EndPopup(); } -bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, bool enabled) +bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut, bool selected, bool enabled) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) @@ -6787,19 +7050,31 @@ bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, boo ImVec2 pos = window->DC.CursorPos; ImVec2 label_size = CalcTextSize(label, NULL, true); + const bool menuset_is_open = IsRootOfOpenMenuSet(); + ImGuiWindow* backed_nav_window = g.NavWindow; + if (menuset_is_open) + g.NavWindow = window; + // We've been using the equivalent of ImGuiSelectableFlags_SetNavIdOnHover on all Selectable() since early Nav system days (commit 43ee5d73), // but I am unsure whether this should be kept at all. For now moved it to be an opt-in feature used by menus only. - ImGuiSelectableFlags flags = ImGuiSelectableFlags_SelectOnRelease | ImGuiSelectableFlags_SetNavIdOnHover | (enabled ? 0 : ImGuiSelectableFlags_Disabled); bool pressed; + PushID(label); + if (!enabled) + BeginDisabled(); + + const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_SelectOnRelease | ImGuiSelectableFlags_SetNavIdOnHover; + const ImGuiMenuColumns* offsets = &window->DC.MenuColumns; if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) { // Mimic the exact layout spacing of BeginMenu() to allow MenuItem() inside a menu bar, which is a little misleading but may be useful - // Note that in this situation we render neither the shortcut neither the selected tick mark + // Note that in this situation: we don't render the shortcut, we render a highlight instead of the selected tick mark. float w = label_size.x; window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * 0.5f); + ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset); PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x * 2.0f, style.ItemSpacing.y)); - pressed = Selectable(label, false, flags, ImVec2(w, 0.0f)); + pressed = Selectable("", selected, selectable_flags, ImVec2(w, 0.0f)); PopStyleVar(); + RenderText(text_pos, label); window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar(). } else @@ -6807,27 +7082,42 @@ bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, boo // Menu item inside a vertical menu // (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f. // Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system. - float shortcut_w = shortcut ? CalcTextSize(shortcut, NULL).x : 0.0f; - float min_w = window->DC.MenuColumns.DeclColumns(label_size.x, shortcut_w, IM_FLOOR(g.FontSize * 1.20f)); // Feedback for next frame - float extra_w = ImMax(0.0f, GetContentRegionAvail().x - min_w); - pressed = Selectable(label, false, flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, 0.0f)); + float icon_w = (icon && icon[0]) ? CalcTextSize(icon, NULL).x : 0.0f; + float shortcut_w = (shortcut && shortcut[0]) ? CalcTextSize(shortcut, NULL).x : 0.0f; + float checkmark_w = IM_FLOOR(g.FontSize * 1.20f); + float min_w = window->DC.MenuColumns.DeclColumns(icon_w, label_size.x, shortcut_w, checkmark_w); // Feedback for next frame + float stretch_w = ImMax(0.0f, GetContentRegionAvail().x - min_w); + pressed = Selectable("", false, selectable_flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, 0.0f)); + RenderText(pos + ImVec2(offsets->OffsetLabel, 0.0f), label); + if (icon_w > 0.0f) + RenderText(pos + ImVec2(offsets->OffsetIcon, 0.0f), icon); if (shortcut_w > 0.0f) { - PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]); - RenderText(pos + ImVec2(window->DC.MenuColumns.Pos[1] + extra_w, 0.0f), shortcut, NULL, false); + PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_TextDisabled]); + RenderText(pos + ImVec2(offsets->OffsetShortcut + stretch_w, 0.0f), shortcut, NULL, false); PopStyleColor(); } if (selected) - RenderCheckMark(window->DrawList, pos + ImVec2(window->DC.MenuColumns.Pos[2] + extra_w + g.FontSize * 0.40f, g.FontSize * 0.134f * 0.5f), GetColorU32(enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled), g.FontSize * 0.866f); + RenderCheckMark(window->DrawList, pos + ImVec2(offsets->OffsetMark + stretch_w + g.FontSize * 0.40f, g.FontSize * 0.134f * 0.5f), GetColorU32(ImGuiCol_Text), g.FontSize * 0.866f); } + IMGUI_TEST_ENGINE_ITEM_INFO(g.LastItemData.ID, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (selected ? ImGuiItemStatusFlags_Checked : 0)); + if (!enabled) + EndDisabled(); + PopID(); + if (menuset_is_open) + g.NavWindow = backed_nav_window; - IMGUI_TEST_ENGINE_ITEM_INFO(window->DC.LastItemId, label, window->DC.ItemFlags | ImGuiItemStatusFlags_Checkable | (selected ? ImGuiItemStatusFlags_Checked : 0)); return pressed; } +bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, bool enabled) +{ + return MenuItemEx(label, NULL, shortcut, selected, enabled); +} + bool ImGui::MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled) { - if (MenuItem(label, shortcut, p_selected ? *p_selected : false, enabled)) + if (MenuItemEx(label, NULL, shortcut, p_selected ? *p_selected : false, enabled)) { if (p_selected) *p_selected = !*p_selected; @@ -6870,7 +7160,7 @@ namespace ImGui static ImU32 TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label); static float TabBarCalcMaxTabWidth(); static float TabBarScrollClamp(ImGuiTabBar* tab_bar, float scrolling); - static void TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab, ImGuiTabBarSection* sections); + static void TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiID tab_id, ImGuiTabBarSection* sections); static ImGuiTabItem* TabBarScrollingButtons(ImGuiTabBar* tab_bar); static ImGuiTabItem* TabBarTabListPopupButton(ImGuiTabBar* tab_bar); } @@ -6882,12 +7172,17 @@ ImGuiTabBar::ImGuiTabBar() LastTabItemIdx = -1; } +static inline int TabItemGetSectionIdx(const ImGuiTabItem* tab) +{ + return (tab->Flags & ImGuiTabItemFlags_Leading) ? 0 : (tab->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1; +} + static int IMGUI_CDECL TabItemComparerBySection(const void* lhs, const void* rhs) { const ImGuiTabItem* a = (const ImGuiTabItem*)lhs; const ImGuiTabItem* b = (const ImGuiTabItem*)rhs; - const int a_section = (a->Flags & ImGuiTabItemFlags_Leading) ? 0 : (a->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1; - const int b_section = (b->Flags & ImGuiTabItemFlags_Leading) ? 0 : (b->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1; + const int a_section = TabItemGetSectionIdx(a); + const int b_section = TabItemGetSectionIdx(b); if (a_section != b_section) return a_section - b_section; return (int)(a->IndexDuringLayout - b->IndexDuringLayout); @@ -6953,8 +7248,7 @@ bool ImGui::BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& tab_bar_bb, ImG // Ensure correct ordering when toggling ImGuiTabBarFlags_Reorderable flag, or when a new tab was added while being not reorderable if ((flags & ImGuiTabBarFlags_Reorderable) != (tab_bar->Flags & ImGuiTabBarFlags_Reorderable) || (tab_bar->TabsAddedNew && !(flags & ImGuiTabBarFlags_Reorderable))) - if (tab_bar->Tabs.Size > 1) - ImQsort(tab_bar->Tabs.Data, tab_bar->Tabs.Size, sizeof(ImGuiTabItem), TabItemComparerByBeginOrder); + ImQsort(tab_bar->Tabs.Data, tab_bar->Tabs.Size, sizeof(ImGuiTabItem), TabItemComparerByBeginOrder); tab_bar->TabsAddedNew = false; // Flags @@ -7056,11 +7350,11 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) tab->IndexDuringLayout = (ImS16)tab_dst_n; // We will need sorting if tabs have changed section (e.g. moved from one of Leading/Central/Trailing to another) - int curr_tab_section_n = (tab->Flags & ImGuiTabItemFlags_Leading) ? 0 : (tab->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1; + int curr_tab_section_n = TabItemGetSectionIdx(tab); if (tab_dst_n > 0) { ImGuiTabItem* prev_tab = &tab_bar->Tabs[tab_dst_n - 1]; - int prev_tab_section_n = (prev_tab->Flags & ImGuiTabItemFlags_Leading) ? 0 : (prev_tab->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1; + int prev_tab_section_n = TabItemGetSectionIdx(prev_tab); if (curr_tab_section_n == 0 && prev_tab_section_n != 0) need_sort_by_section = true; if (prev_tab_section_n == 2 && curr_tab_section_n != 2) @@ -7081,12 +7375,12 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) sections[1].Spacing = sections[1].TabCount > 0 && sections[2].TabCount > 0 ? g.Style.ItemInnerSpacing.x : 0.0f; // Setup next selected tab - ImGuiID scroll_track_selected_tab_id = 0; + ImGuiID scroll_to_tab_id = 0; if (tab_bar->NextSelectedTabId) { tab_bar->SelectedTabId = tab_bar->NextSelectedTabId; tab_bar->NextSelectedTabId = 0; - scroll_track_selected_tab_id = tab_bar->SelectedTabId; + scroll_to_tab_id = tab_bar->SelectedTabId; } // Process order change request (we could probably process it when requested but it's just saner to do it in a single spot). @@ -7094,7 +7388,7 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) { if (TabBarProcessReorder(tab_bar)) if (tab_bar->ReorderRequestTabId == tab_bar->SelectedTabId) - scroll_track_selected_tab_id = tab_bar->ReorderRequestTabId; + scroll_to_tab_id = tab_bar->ReorderRequestTabId; tab_bar->ReorderRequestTabId = 0; } @@ -7102,7 +7396,7 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) const bool tab_list_popup_button = (tab_bar->Flags & ImGuiTabBarFlags_TabListPopupButton) != 0; if (tab_list_popup_button) if (ImGuiTabItem* tab_to_select = TabBarTabListPopupButton(tab_bar)) // NB: Will alter BarRect.Min.x! - scroll_track_selected_tab_id = tab_bar->SelectedTabId = tab_to_select->ID; + scroll_to_tab_id = tab_bar->SelectedTabId = tab_to_select->ID; // Leading/Trailing tabs will be shrink only if central one aren't visible anymore, so layout the shrink data as: leading, trailing, central // (whereas our tabs are stored as: leading, central, trailing) @@ -7122,8 +7416,8 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) most_recently_selected_tab = tab; if (tab->ID == tab_bar->SelectedTabId) found_selected_tab_id = true; - if (scroll_track_selected_tab_id == 0 && g.NavJustMovedToId == tab->ID) - scroll_track_selected_tab_id = tab->ID; + if (scroll_to_tab_id == 0 && g.NavJustMovedToId == tab->ID) + scroll_to_tab_id = tab->ID; // Refresh tab width immediately, otherwise changes of style e.g. style.FramePadding.x would noticeably lag in the tab bar. // Additionally, when using TabBarAddTab() to manipulate tab bar order we occasionally insert new tabs that don't have a width yet, @@ -7132,12 +7426,13 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) const bool has_close_button = (tab->Flags & ImGuiTabItemFlags_NoCloseButton) ? false : true; tab->ContentWidth = TabItemCalcSize(tab_name, has_close_button).x; - int section_n = (tab->Flags & ImGuiTabItemFlags_Leading) ? 0 : (tab->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1; + int section_n = TabItemGetSectionIdx(tab); ImGuiTabBarSection* section = §ions[section_n]; section->Width += tab->ContentWidth + (section_n == curr_section_n ? g.Style.ItemInnerSpacing.x : 0.0f); curr_section_n = section_n; // Store data so we can build an array sorted by width if we need to shrink tabs down + IM_MSVC_WARNING_SUPPRESS(6385); int shrink_buffer_index = shrink_buffer_indexes[section_n]++; g.ShrinkWidthBuffer[shrink_buffer_index].Index = tab_n; g.ShrinkWidthBuffer[shrink_buffer_index].Width = tab->ContentWidth; @@ -7154,11 +7449,11 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) // Horizontal scrolling buttons // (note that TabBarScrollButtons() will alter BarRect.Max.x) if ((tab_bar->WidthAllTabsIdeal > tab_bar->BarRect.GetWidth() && tab_bar->Tabs.Size > 1) && !(tab_bar->Flags & ImGuiTabBarFlags_NoTabListScrollingButtons) && (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyScroll)) - if (ImGuiTabItem* scroll_track_selected_tab = TabBarScrollingButtons(tab_bar)) + if (ImGuiTabItem* scroll_and_select_tab = TabBarScrollingButtons(tab_bar)) { - scroll_track_selected_tab_id = scroll_track_selected_tab->ID; - if (!(scroll_track_selected_tab->Flags & ImGuiTabItemFlags_Button)) - tab_bar->SelectedTabId = scroll_track_selected_tab_id; + scroll_to_tab_id = scroll_and_select_tab->ID; + if ((scroll_and_select_tab->Flags & ImGuiTabItemFlags_Button) == 0) + tab_bar->SelectedTabId = scroll_to_tab_id; } // Shrink widths if full tabs don't fit in their allocated space @@ -7187,7 +7482,7 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) if (shrinked_width < 0.0f) continue; - int section_n = (tab->Flags & ImGuiTabItemFlags_Leading) ? 0 : (tab->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1; + int section_n = TabItemGetSectionIdx(tab); sections[section_n].Width -= (tab->Width - shrinked_width); tab->Width = shrinked_width; } @@ -7218,16 +7513,15 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) if (found_selected_tab_id == false) tab_bar->SelectedTabId = 0; if (tab_bar->SelectedTabId == 0 && tab_bar->NextSelectedTabId == 0 && most_recently_selected_tab != NULL) - scroll_track_selected_tab_id = tab_bar->SelectedTabId = most_recently_selected_tab->ID; + scroll_to_tab_id = tab_bar->SelectedTabId = most_recently_selected_tab->ID; // Lock in visible tab tab_bar->VisibleTabId = tab_bar->SelectedTabId; tab_bar->VisibleTabWasSubmitted = false; // Update scrolling - if (scroll_track_selected_tab_id) - if (ImGuiTabItem* scroll_track_selected_tab = TabBarFindTabByID(tab_bar, scroll_track_selected_tab_id)) - TabBarScrollToTab(tab_bar, scroll_track_selected_tab, sections); + if (scroll_to_tab_id != 0) + TabBarScrollToTab(tab_bar, scroll_to_tab_id, sections); tab_bar->ScrollingAnim = TabBarScrollClamp(tab_bar, tab_bar->ScrollingAnim); tab_bar->ScrollingTarget = TabBarScrollClamp(tab_bar, tab_bar->ScrollingTarget); if (tab_bar->ScrollingAnim != tab_bar->ScrollingTarget) @@ -7327,9 +7621,13 @@ static float ImGui::TabBarScrollClamp(ImGuiTabBar* tab_bar, float scrolling) return ImMax(scrolling, 0.0f); } -static void ImGui::TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab, ImGuiTabBarSection* sections) +// Note: we may scroll to tab that are not selected! e.g. using keyboard arrow keys +static void ImGui::TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiID tab_id, ImGuiTabBarSection* sections) { - if (tab->Flags & (ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing)) + ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_id); + if (tab == NULL) + return; + if (tab->Flags & ImGuiTabItemFlags_SectionMask_) return; ImGuiContext& g = *GImGui; @@ -7358,12 +7656,48 @@ static void ImGui::TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab, Im } } -void ImGui::TabBarQueueReorder(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, int dir) +void ImGui::TabBarQueueReorder(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, int offset) { - IM_ASSERT(dir == -1 || dir == +1); + IM_ASSERT(offset != 0); IM_ASSERT(tab_bar->ReorderRequestTabId == 0); tab_bar->ReorderRequestTabId = tab->ID; - tab_bar->ReorderRequestDir = (ImS8)dir; + tab_bar->ReorderRequestOffset = (ImS16)offset; +} + +void ImGui::TabBarQueueReorderFromMousePos(ImGuiTabBar* tab_bar, const ImGuiTabItem* src_tab, ImVec2 mouse_pos) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(tab_bar->ReorderRequestTabId == 0); + if ((tab_bar->Flags & ImGuiTabBarFlags_Reorderable) == 0) + return; + + const bool is_central_section = (src_tab->Flags & ImGuiTabItemFlags_SectionMask_) == 0; + const float bar_offset = tab_bar->BarRect.Min.x - (is_central_section ? tab_bar->ScrollingTarget : 0); + + // Count number of contiguous tabs we are crossing over + const int dir = (bar_offset + src_tab->Offset) > mouse_pos.x ? -1 : +1; + const int src_idx = tab_bar->Tabs.index_from_ptr(src_tab); + int dst_idx = src_idx; + for (int i = src_idx; i >= 0 && i < tab_bar->Tabs.Size; i += dir) + { + // Reordered tabs must share the same section + const ImGuiTabItem* dst_tab = &tab_bar->Tabs[i]; + if (dst_tab->Flags & ImGuiTabItemFlags_NoReorder) + break; + if ((dst_tab->Flags & ImGuiTabItemFlags_SectionMask_) != (src_tab->Flags & ImGuiTabItemFlags_SectionMask_)) + break; + dst_idx = i; + + // Include spacing after tab, so when mouse cursor is between tabs we would not continue checking further tabs that are not hovered. + const float x1 = bar_offset + dst_tab->Offset - g.Style.ItemInnerSpacing.x; + const float x2 = bar_offset + dst_tab->Offset + dst_tab->Width + g.Style.ItemInnerSpacing.x; + //GetForegroundDrawList()->AddRect(ImVec2(x1, tab_bar->BarRect.Min.y), ImVec2(x2, tab_bar->BarRect.Max.y), IM_COL32(255, 0, 0, 255)); + if ((dir < 0 && mouse_pos.x > x1) || (dir > 0 && mouse_pos.x < x2)) + break; + } + + if (dst_idx != src_idx) + TabBarQueueReorder(tab_bar, src_tab, dst_idx - src_idx); } bool ImGui::TabBarProcessReorder(ImGuiTabBar* tab_bar) @@ -7373,19 +7707,23 @@ bool ImGui::TabBarProcessReorder(ImGuiTabBar* tab_bar) return false; //IM_ASSERT(tab_bar->Flags & ImGuiTabBarFlags_Reorderable); // <- this may happen when using debug tools - int tab2_order = tab_bar->GetTabOrder(tab1) + tab_bar->ReorderRequestDir; + int tab2_order = tab_bar->GetTabOrder(tab1) + tab_bar->ReorderRequestOffset; if (tab2_order < 0 || tab2_order >= tab_bar->Tabs.Size) return false; - // Reordered TabItem must share the same position flags than target + // Reordered tabs must share the same section + // (Note: TabBarQueueReorderFromMousePos() also has a similar test but since we allow direct calls to TabBarQueueReorder() we do it here too) ImGuiTabItem* tab2 = &tab_bar->Tabs[tab2_order]; if (tab2->Flags & ImGuiTabItemFlags_NoReorder) return false; - if ((tab1->Flags & (ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing)) != (tab2->Flags & (ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing))) + if ((tab1->Flags & ImGuiTabItemFlags_SectionMask_) != (tab2->Flags & ImGuiTabItemFlags_SectionMask_)) return false; ImGuiTabItem item_tmp = *tab1; - *tab1 = *tab2; + ImGuiTabItem* src_tab = (tab_bar->ReorderRequestOffset > 0) ? tab1 + 1 : tab2; + ImGuiTabItem* dst_tab = (tab_bar->ReorderRequestOffset > 0) ? tab1 : tab2 + 1; + const int move_count = (tab_bar->ReorderRequestOffset > 0) ? tab_bar->ReorderRequestOffset : -tab_bar->ReorderRequestOffset; + memmove(dst_tab, src_tab, move_count * sizeof(ImGuiTabItem)); *tab2 = item_tmp; if (tab_bar->Flags & ImGuiTabBarFlags_SaveSettings) @@ -7580,12 +7918,10 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, // If the user called us with *p_open == false, we early out and don't render. // We make a call to ItemAdd() so that attempts to use a contextual popup menu with an implicit ID won't use an older ID. - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.LastItemStatusFlags); + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); if (p_open && !*p_open) { - PushItemFlag(ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus, true); - ItemAdd(ImRect(), id); - PopItemFlag(); + ItemAdd(ImRect(), id, NULL, ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus); return false; } @@ -7625,7 +7961,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, tab->Flags = flags; // Append name with zero-terminator - tab->NameOffset = (ImS16)tab_bar->TabsNames.size(); + tab->NameOffset = (ImS32)tab_bar->TabsNames.size(); tab_bar->TabsNames.append(label, label + strlen(label) + 1); // Update selected tab @@ -7652,9 +7988,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, // and then gets submitted again, the tabs will have 'tab_appearing=true' but 'tab_is_new=false'. if (tab_appearing && (!tab_bar_appearing || tab_is_new)) { - PushItemFlag(ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus, true); - ItemAdd(ImRect(), id); - PopItemFlag(); + ItemAdd(ImRect(), id, NULL, ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus); if (is_tab_button) return false; return tab_contents_visible; @@ -7667,7 +8001,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, const ImVec2 backup_main_cursor_pos = window->DC.CursorPos; // Layout - const bool is_central_section = (tab->Flags & (ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing)) == 0; + const bool is_central_section = (tab->Flags & ImGuiTabItemFlags_SectionMask_) == 0; size.x = tab->Width; if (is_central_section) window->DC.CursorPos = tab_bar->BarRect.Min + ImVec2(IM_FLOOR(tab->Offset - tab_bar->ScrollingAnim), 0.0f); @@ -7701,7 +8035,6 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags); if (pressed && !is_tab_button) tab_bar->NextSelectedTabId = id; - hovered |= (g.HoveredId == id); // Allow the close button to overlap unless we are dragging (in which case we don't want any overlapping tabs to be hovered) if (g.ActiveId != id) @@ -7715,13 +8048,11 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, // While moving a tab it will jump on the other side of the mouse, so we also test for MouseDelta.x if (g.IO.MouseDelta.x < 0.0f && g.IO.MousePos.x < bb.Min.x) { - if (tab_bar->Flags & ImGuiTabBarFlags_Reorderable) - TabBarQueueReorder(tab_bar, tab, -1); + TabBarQueueReorderFromMousePos(tab_bar, tab, g.IO.MousePos); } else if (g.IO.MouseDelta.x > 0.0f && g.IO.MousePos.x > bb.Max.x) { - if (tab_bar->Flags & ImGuiTabBarFlags_Reorderable) - TabBarQueueReorder(tab_bar, tab, +1); + TabBarQueueReorderFromMousePos(tab_bar, tab, g.IO.MousePos); } } } @@ -7767,8 +8098,11 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, PopClipRect(); window->DC.CursorPos = backup_main_cursor_pos; - // Tooltip (FIXME: Won't work over the close button because ItemOverlap systems messes up with HoveredIdTimer) - // We test IsItemHovered() to discard e.g. when another item is active or drag and drop over the tab bar (which g.HoveredId ignores) + // Tooltip + // (Won't work over the close button because ItemOverlap systems messes up with HoveredIdTimer-> seems ok) + // (We test IsItemHovered() to discard e.g. when another item is active or drag and drop over the tab bar, which g.HoveredId ignores) + // FIXME: This is a mess. + // FIXME: We may want disabled tab to still display the tooltip? if (text_clipped && g.HoveredId == id && !held && g.HoveredIdNotActiveTimer > g.TooltipSlowDelay && IsItemHovered()) if (!(tab_bar->Flags & ImGuiTabBarFlags_NoTooltip) && !(tab->Flags & ImGuiTabItemFlags_NoTooltip)) SetTooltip("%.*s", (int)(FindRenderedTextEnd(label) - label), label); @@ -7828,7 +8162,7 @@ void ImGui::TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabI draw_list->PathArcToFast(ImVec2(bb.Min.x + rounding + 0.5f, y1 + rounding + 0.5f), rounding, 6, 9); draw_list->PathArcToFast(ImVec2(bb.Max.x - rounding - 0.5f, y1 + rounding + 0.5f), rounding, 9, 12); draw_list->PathLineTo(ImVec2(bb.Max.x - 0.5f, y2)); - draw_list->PathStroke(GetColorU32(ImGuiCol_Border), false, g.Style.TabBorderSize); + draw_list->PathStroke(GetColorU32(ImGuiCol_Border), 0, g.Style.TabBorderSize); } } @@ -7856,14 +8190,7 @@ void ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, #endif // Render text label (with clipping + alpha gradient) + unsaved marker - const char* TAB_UNSAVED_MARKER = "*"; ImRect text_pixel_clip_bb(bb.Min.x + frame_padding.x, bb.Min.y + frame_padding.y, bb.Max.x - frame_padding.x, bb.Max.y); - if (flags & ImGuiTabItemFlags_UnsavedDocument) - { - text_pixel_clip_bb.Max.x -= CalcTextSize(TAB_UNSAVED_MARKER, NULL, false).x; - ImVec2 unsaved_marker_pos(ImMin(bb.Min.x + frame_padding.x + label_size.x + 2, text_pixel_clip_bb.Max.x), bb.Min.y + frame_padding.y + IM_FLOOR(-g.FontSize * 0.25f)); - RenderTextClippedEx(draw_list, unsaved_marker_pos, bb.Max - frame_padding, TAB_UNSAVED_MARKER, NULL, NULL); - } ImRect text_ellipsis_clip_bb = text_pixel_clip_bb; // Return clipped state ignoring the close button @@ -7873,7 +8200,10 @@ void ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, //draw_list->AddCircle(text_ellipsis_clip_bb.Min, 3.0f, *out_text_clipped ? IM_COL32(255, 0, 0, 255) : IM_COL32(0, 255, 0, 255)); } - // Close Button + const float button_sz = g.FontSize; + const ImVec2 button_pos(ImMax(bb.Min.x, bb.Max.x - frame_padding.x * 2.0f - button_sz), bb.Min.y); + + // Close Button & Unsaved Marker // We are relying on a subtle and confusing distinction between 'hovered' and 'g.HoveredId' which happens because we are using ImGuiButtonFlags_AllowOverlapMode + SetItemAllowOverlap() // 'hovered' will be true when hovering the Tab but NOT when hovering the close button // 'g.HoveredId==id' will be true when hovering the Tab including when hovering the close button @@ -7881,28 +8211,40 @@ void ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, bool close_button_pressed = false; bool close_button_visible = false; if (close_button_id != 0) - if (is_contents_visible || bb.GetWidth() >= g.Style.TabMinWidthForCloseButton) + if (is_contents_visible || bb.GetWidth() >= ImMax(button_sz, g.Style.TabMinWidthForCloseButton)) if (g.HoveredId == tab_id || g.HoveredId == close_button_id || g.ActiveId == tab_id || g.ActiveId == close_button_id) close_button_visible = true; + bool unsaved_marker_visible = (flags & ImGuiTabItemFlags_UnsavedDocument) != 0 && (button_pos.x + button_sz <= bb.Max.x); + if (close_button_visible) { - ImGuiLastItemDataBackup last_item_backup; - const float close_button_sz = g.FontSize; + ImGuiLastItemData last_item_backup = g.LastItemData; PushStyleVar(ImGuiStyleVar_FramePadding, frame_padding); - if (CloseButton(close_button_id, ImVec2(bb.Max.x - frame_padding.x * 2.0f - close_button_sz, bb.Min.y))) + if (CloseButton(close_button_id, button_pos)) close_button_pressed = true; PopStyleVar(); - last_item_backup.Restore(); + g.LastItemData = last_item_backup; // Close with middle mouse button if (!(flags & ImGuiTabItemFlags_NoCloseWithMiddleMouseButton) && IsMouseClicked(2)) close_button_pressed = true; - - text_pixel_clip_bb.Max.x -= close_button_sz; + } + else if (unsaved_marker_visible) + { + const ImRect bullet_bb(button_pos, button_pos + ImVec2(button_sz, button_sz) + g.Style.FramePadding * 2.0f); + RenderBullet(draw_list, bullet_bb.GetCenter(), GetColorU32(ImGuiCol_Text)); } + // This is all rather complicated + // (the main idea is that because the close button only appears on hover, we don't want it to alter the ellipsis position) // FIXME: if FramePadding is noticeably large, ellipsis_max_x will be wrong here (e.g. #3497), maybe for consistency that parameter of RenderTextEllipsis() shouldn't exist.. float ellipsis_max_x = close_button_visible ? text_pixel_clip_bb.Max.x : bb.Max.x - 1.0f; + if (close_button_visible || unsaved_marker_visible) + { + text_pixel_clip_bb.Max.x -= close_button_visible ? (button_sz) : (button_sz * 0.80f); + text_ellipsis_clip_bb.Max.x -= unsaved_marker_visible ? (button_sz * 0.80f) : 0.0f; + ellipsis_max_x = text_pixel_clip_bb.Max.x; + } RenderTextEllipsis(draw_list, text_ellipsis_clip_bb.Min, text_ellipsis_clip_bb.Max, text_pixel_clip_bb.Max.x, ellipsis_max_x, label, NULL, &label_size); #if 0 diff --git a/apps/common/external/imgui/imstb_rectpack.h b/apps/common/external/imgui/imstb_rectpack.h index ff2a85df42..3958952162 100644 --- a/apps/common/external/imgui/imstb_rectpack.h +++ b/apps/common/external/imgui/imstb_rectpack.h @@ -34,7 +34,7 @@ // Minor features // Martins Mozeiko // github:IntellectualKitty -// +// // Bugfixes / warning fixes // Jeremy Jaussaud // Fabian Giesen @@ -441,7 +441,7 @@ static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int widt } } tail = tail->next; - } + } } fr.prev_link = best; @@ -602,38 +602,38 @@ This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------ ALTERNATIVE A - MIT License Copyright (c) 2017 Sean Barrett -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------ ALTERNATIVE B - Public Domain (www.unlicense.org) This is free and unencumbered software released into the public domain. -Anyone is free to copy, modify, publish, use, compile, sell, or distribute this -software, either in source code form or as a compiled binary, for any purpose, +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. -In jurisdictions that recognize copyright laws, the author or authors of this -software dedicate any and all copyright interest in the software to the public -domain. We make this dedication for the benefit of the public at large and to -the detriment of our heirs and successors. We intend this dedication to be an -overt act of relinquishment in perpetuity of all present and future rights to +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------ */ diff --git a/apps/common/external/imgui/imstb_textedit.h b/apps/common/external/imgui/imstb_textedit.h index 7644670975..2c635b27d0 100644 --- a/apps/common/external/imgui/imstb_textedit.h +++ b/apps/common/external/imgui/imstb_textedit.h @@ -1,5 +1,5 @@ // [DEAR IMGUI] -// This is a slightly modified version of stb_textedit.h 1.13. +// This is a slightly modified version of stb_textedit.h 1.13. // Those changes would need to be pushed into nothings/stb: // - Fix in stb_textedit_discard_redo (see https://github.com/nothings/stb/issues/321) // Grep for [DEAR IMGUI] to find the changes. @@ -19,7 +19,7 @@ // texts, as its performance does not scale and it has limited undo). // // Non-trivial behaviors are modelled after Windows text controls. -// +// // // LICENSE // @@ -217,20 +217,20 @@ // call this with the mouse x,y on a mouse down; it will update the cursor // and reset the selection start/end to the cursor point. the x,y must // be relative to the text widget, with (0,0) being the top left. -// +// // drag: // call this with the mouse x,y on a mouse drag/up; it will update the // cursor and the selection end point -// +// // cut: // call this to delete the current selection; returns true if there was // one. you should FIRST copy the current selection to the system paste buffer. // (To copy, just copy the current selection out of the string yourself.) -// +// // paste: // call this to paste text at the current cursor point or over the current // selection if there is one. -// +// // key: // call this for keyboard inputs sent to the textfield. you can use it // for "key down" events or for "translated" key events. if you need to @@ -241,7 +241,7 @@ // clear. STB_TEXTEDIT_KEYTYPE defaults to int, but you can #define it to // anything other type you wante before including. // -// +// // When rendering, you can read the cursor position and selection state from // the STB_TexteditState. // @@ -716,9 +716,11 @@ static int stb_textedit_paste_internal(STB_TEXTEDIT_STRING *str, STB_TexteditSta state->has_preferred_x = 0; return 1; } - // remove the undo since we didn't actually insert the characters - if (state->undostate.undo_point) - --state->undostate.undo_point; + // [DEAR IMGUI] + //// remove the undo since we didn't actually insert the characters + //if (state->undostate.undo_point) + // --state->undostate.undo_point; + // note: paste failure will leave deleted selection, may be restored with an undo (see https://github.com/nothings/stb/issues/734 for details) return 0; } @@ -764,7 +766,7 @@ static void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, state->insert_mode = !state->insert_mode; break; #endif - + case STB_TEXTEDIT_K_UNDO: stb_text_undo(str, state); state->has_preferred_x = 0; @@ -779,7 +781,7 @@ static void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, // if currently there's a selection, move cursor to start of selection if (STB_TEXT_HAS_SELECTION(state)) stb_textedit_move_to_first(state); - else + else if (state->cursor > 0) --state->cursor; state->has_preferred_x = 0; @@ -828,7 +830,7 @@ static void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, #ifdef STB_TEXTEDIT_MOVEWORDRIGHT case STB_TEXTEDIT_K_WORDRIGHT: - if (STB_TEXT_HAS_SELECTION(state)) + if (STB_TEXT_HAS_SELECTION(state)) stb_textedit_move_to_last(str, state); else { state->cursor = STB_TEXTEDIT_MOVEWORDRIGHT(str, state->cursor); @@ -922,7 +924,7 @@ static void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, } break; } - + case STB_TEXTEDIT_K_UP: case STB_TEXTEDIT_K_UP | STB_TEXTEDIT_K_SHIFT: case STB_TEXTEDIT_K_PGUP: @@ -1014,7 +1016,7 @@ static void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, } state->has_preferred_x = 0; break; - + #ifdef STB_TEXTEDIT_K_TEXTSTART2 case STB_TEXTEDIT_K_TEXTSTART2: #endif @@ -1031,7 +1033,7 @@ static void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, state->select_start = state->select_end = 0; state->has_preferred_x = 0; break; - + #ifdef STB_TEXTEDIT_K_TEXTSTART2 case STB_TEXTEDIT_K_TEXTSTART2 | STB_TEXTEDIT_K_SHIFT: #endif @@ -1410,38 +1412,38 @@ This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------ ALTERNATIVE A - MIT License Copyright (c) 2017 Sean Barrett -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------ ALTERNATIVE B - Public Domain (www.unlicense.org) This is free and unencumbered software released into the public domain. -Anyone is free to copy, modify, publish, use, compile, sell, or distribute this -software, either in source code form or as a compiled binary, for any purpose, +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. -In jurisdictions that recognize copyright laws, the author or authors of this -software dedicate any and all copyright interest in the software to the public -domain. We make this dedication for the benefit of the public at large and to -the detriment of our heirs and successors. We intend this dedication to be an -overt act of relinquishment in perpetuity of all present and future rights to +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------ */ diff --git a/apps/common/external/imgui/imstb_truetype.h b/apps/common/external/imgui/imstb_truetype.h index b4bdbd8694..48c2026176 100644 --- a/apps/common/external/imgui/imstb_truetype.h +++ b/apps/common/external/imgui/imstb_truetype.h @@ -51,7 +51,7 @@ // Rob Loach Cort Stratton // Kenney Phillis Jr. github:oyvindjam // Brian Costabile github:vassvik -// +// // VERSION HISTORY // // 1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics() @@ -212,7 +212,7 @@ // // Advancing for the next character: // Call GlyphHMetrics, and compute 'current_point += SF * advance'. -// +// // // ADVANCED USAGE // @@ -257,7 +257,7 @@ // Curve tessellation 120 LOC \__ 550 LOC Bitmap creation // Bitmap management 100 LOC / // Baked bitmap interface 70 LOC / -// Font name matching & access 150 LOC ---- 150 +// Font name matching & access 150 LOC ---- 150 // C runtime library abstraction 60 LOC ---- 60 // // @@ -350,7 +350,7 @@ int main(int argc, char **argv) } return 0; } -#endif +#endif // // Output: // @@ -364,9 +364,9 @@ int main(int argc, char **argv) // :@@. M@M // @@@o@@@@ // :M@@V:@@. -// +// ////////////////////////////////////////////////////////////////////////////// -// +// // Complete program: print "Hello World!" banner, with bugs // #if 0 @@ -667,7 +667,7 @@ STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, cons // Calling these functions in sequence is roughly equivalent to calling // stbtt_PackFontRanges(). If you more control over the packing of multiple // fonts, or if you want to pack custom data into a font texture, take a look -// at the source to of stbtt_PackFontRanges() and create a custom version +// at the source to of stbtt_PackFontRanges() and create a custom version // using these functions, e.g. call GatherRects multiple times, // building up a single array of rects, then call PackRects once, // then call RenderIntoRects repeatedly. This may result in a @@ -975,7 +975,7 @@ STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, floa // and computing from that can allow drop-out prevention). // // The algorithm has not been optimized at all, so expect it to be slow -// if computing lots of characters or very large sizes. +// if computing lots of characters or very large sizes. @@ -1732,7 +1732,7 @@ static int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, s if (i != 0) num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); - // now start the new one + // now start the new one start_off = !(flags & 1); if (start_off) { // if we start off with an off-curve point, then when we need to find a point on the curve @@ -1785,7 +1785,7 @@ static int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, s int comp_num_verts = 0, i; stbtt_vertex *comp_verts = 0, *tmp = 0; float mtx[6] = {1,0,0,1,0,0}, m, n; - + flags = ttSHORT(comp); comp+=2; gidx = ttSHORT(comp); comp+=2; @@ -1815,7 +1815,7 @@ static int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, s mtx[2] = ttSHORT(comp)/16384.0f; comp+=2; mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; } - + // Find transformation scales. m = (float) STBTT_sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]); n = (float) STBTT_sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]); @@ -2746,7 +2746,7 @@ static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, i float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); STBTT_assert(z != NULL); if (!z) return z; - + // round dx down to avoid overshooting if (dxdy < 0) z->dx = -STBTT_ifloor(STBTT_FIX * -dxdy); @@ -2824,7 +2824,7 @@ static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__ac } } } - + e = e->next; } } @@ -3554,7 +3554,7 @@ STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info { int ix0,iy0,ix1,iy1; stbtt__bitmap gbm; - stbtt_vertex *vertices; + stbtt_vertex *vertices; int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); if (scale_x == 0) scale_x = scale_y; @@ -3577,7 +3577,7 @@ STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info if (height) *height = gbm.h; if (xoff ) *xoff = ix0; if (yoff ) *yoff = iy0; - + if (gbm.w && gbm.h) { gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata); if (gbm.pixels) { @@ -3588,7 +3588,7 @@ STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info } STBTT_free(vertices, info->userdata); return gbm.pixels; -} +} STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff) { @@ -3600,7 +3600,7 @@ STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigne int ix0,iy0; stbtt_vertex *vertices; int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); - stbtt__bitmap gbm; + stbtt__bitmap gbm; stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0); gbm.pixels = output; @@ -3622,7 +3622,7 @@ STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char * STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff) { return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff); -} +} STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint) { @@ -3637,7 +3637,7 @@ STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, uns STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff) { return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff); -} +} STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint) { @@ -3762,7 +3762,7 @@ static void stbrp_init_target(stbrp_context *con, int pw, int ph, stbrp_node *no con->y = 0; con->bottom_y = 0; STBTT__NOTUSED(nodes); - STBTT__NOTUSED(num_nodes); + STBTT__NOTUSED(num_nodes); } static void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rects) @@ -4147,7 +4147,7 @@ STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char n = 0; for (i=0; i < num_ranges; ++i) n += ranges[i].num_chars; - + rects = (stbrp_rect *) STBTT_malloc(sizeof(*rects) * n, spc->user_allocator_context); if (rects == NULL) return 0; @@ -4158,7 +4158,7 @@ STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char n = stbtt_PackFontRangesGatherRects(spc, &info, ranges, num_ranges, rects); stbtt_PackFontRangesPackRects(spc, rects, n); - + return_value = stbtt_PackFontRangesRenderIntoRects(spc, &info, ranges, num_ranges, rects); STBTT_free(rects, spc->user_allocator_context); @@ -4302,7 +4302,7 @@ static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex int winding = 0; orig[0] = x; - //orig[1] = y; // [DEAR IMGUI] commmented double assignment + //orig[1] = y; // [DEAR IMGUI] commented double assignment // make sure y never passes through a vertex of the shape y_frac = (float) STBTT_fmod(y, 1.0f); @@ -4319,7 +4319,7 @@ static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex int x1 = (int) verts[i ].x, y1 = (int) verts[i ].y; if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0; - if (x_inter < x) + if (x_inter < x) winding += (y0 < y1) ? 1 : -1; } } @@ -4345,7 +4345,7 @@ static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex y1 = (int)verts[i ].y; if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0; - if (x_inter < x) + if (x_inter < x) winding += (y0 < y1) ? 1 : -1; } } else { @@ -4357,7 +4357,7 @@ static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex if (hits[1][0] < 0) winding += (hits[1][1] < 0 ? -1 : 1); } - } + } } } return winding; @@ -4438,7 +4438,7 @@ STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float sc // invert for y-downwards bitmaps scale_y = -scale_y; - + { int x,y,i,j; float *precompute; @@ -4587,7 +4587,7 @@ STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float sc STBTT_free(verts, info->userdata); } return data; -} +} STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff) { @@ -4605,7 +4605,7 @@ STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata) // // check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string -static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, stbtt_int32 len1, stbtt_uint8 *s2, stbtt_int32 len2) +static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, stbtt_int32 len1, stbtt_uint8 *s2, stbtt_int32 len2) { stbtt_int32 i=0; @@ -4644,7 +4644,7 @@ static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, s return i; } -static int stbtt_CompareUTF8toUTF16_bigendian_internal(char *s1, int len1, char *s2, int len2) +static int stbtt_CompareUTF8toUTF16_bigendian_internal(char *s1, int len1, char *s2, int len2) { return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((stbtt_uint8*) s1, len1, (stbtt_uint8*) s2, len2); } @@ -4773,7 +4773,7 @@ STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index) { - return stbtt_GetFontOffsetForIndex_internal((unsigned char *) data, index); + return stbtt_GetFontOffsetForIndex_internal((unsigned char *) data, index); } STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data) @@ -4866,38 +4866,38 @@ This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------ ALTERNATIVE A - MIT License Copyright (c) 2017 Sean Barrett -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------ ALTERNATIVE B - Public Domain (www.unlicense.org) This is free and unencumbered software released into the public domain. -Anyone is free to copy, modify, publish, use, compile, sell, or distribute this -software, either in source code form or as a compiled binary, for any purpose, +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. -In jurisdictions that recognize copyright laws, the author or authors of this -software dedicate any and all copyright interest in the software to the public -domain. We make this dedication for the benefit of the public at large and to -the detriment of our heirs and successors. We intend this dedication to be an -overt act of relinquishment in perpetuity of all present and future rights to +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------ */ diff --git a/apps/common/external/stb_image/stb_image.h b/apps/common/external/stb_image/stb_image.h index accef4839c..d60371b95f 100644 --- a/apps/common/external/stb_image/stb_image.h +++ b/apps/common/external/stb_image/stb_image.h @@ -1,4 +1,4 @@ -/* stb_image - v2.26 - public domain image loader - http://nothings.org/stb +/* stb_image - v2.27 - public domain image loader - http://nothings.org/stb no warranty implied; use at your own risk Do this: @@ -48,6 +48,7 @@ LICENSE RECENT REVISION HISTORY: + 2.27 (2021-07-11) document stbi_info better, 16-bit PNM support, bug fixes 2.26 (2020-07-13) many minor fixes 2.25 (2020-02-02) fix warnings 2.24 (2020-02-02) fix warnings; thread-local failure_reason and flip_vertically @@ -89,7 +90,7 @@ RECENT REVISION HISTORY: Jeremy Sawicki (handle all ImageNet JPGs) Optimizations & bugfixes Mikhail Morozov (1-bit BMP) Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query) - Arseny Kapoulkine + Arseny Kapoulkine Simon Breuss (16-bit PNM) John-Mark Allen Carmelo J Fdez-Aguera @@ -102,7 +103,7 @@ RECENT REVISION HISTORY: Thomas Ruf Ronny Chevalier github:rlyeh Janez Zemva John Bartholomew Michal Cichon github:romigrou Jonathan Blow Ken Hamada Tero Hanninen github:svdijk - Laurent Gomila Cort Stratton github:snagar + Eugene Golushkov Laurent Gomila Cort Stratton github:snagar Aruelien Pocheville Sergio Gonzalez Thibault Reuille github:Zelex Cass Everitt Ryamond Barbiero github:grim210 Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw @@ -110,11 +111,13 @@ RECENT REVISION HISTORY: Josh Tobin Matthew Gregan github:poppolopoppo Julian Raschke Gregory Mullen Christian Floisand github:darealshinji Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007 - Brad Weinberger Matvey Cherevko [reserved] + Brad Weinberger Matvey Cherevko github:mosra Luca Sas Alexander Veselov Zack Middleton [reserved] Ryan C. Gordon [reserved] [reserved] DO NOT ADD YOUR NAME HERE + Jacko Dirks + To add your name to the credits, pick a random blank space in the middle and fill it. 80% of merge conflicts on stb PRs are due to people adding their name at the end of the credits. @@ -176,6 +179,32 @@ RECENT REVISION HISTORY: // // Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. // +// To query the width, height and component count of an image without having to +// decode the full file, you can use the stbi_info family of functions: +// +// int x,y,n,ok; +// ok = stbi_info(filename, &x, &y, &n); +// // returns ok=1 and sets x, y, n if image is a supported format, +// // 0 otherwise. +// +// Note that stb_image pervasively uses ints in its public API for sizes, +// including sizes of memory buffers. This is now part of the API and thus +// hard to change without causing breakage. As a result, the various image +// loaders all have certain limits on image size; these differ somewhat +// by format but generally boil down to either just under 2GB or just under +// 1GB. When the decoded image would be larger than this, stb_image decoding +// will fail. +// +// Additionally, stb_image will reject image files that have any of their +// dimensions set to a larger value than the configurable STBI_MAX_DIMENSIONS, +// which defaults to 2**24 = 16777216 pixels. Due to the above memory limit, +// the only way to have an image with such dimensions load correctly +// is for it to have a rather extreme aspect ratio. Either way, the +// assumption here is that such larger images are likely to be malformed +// or malicious. If you do need to load an image with individual dimensions +// larger than that, and it still fits in the overall size limit, you can +// #define STBI_MAX_DIMENSIONS on your own to be something larger. +// // =========================================================================== // // UNICODE: @@ -281,11 +310,10 @@ RECENT REVISION HISTORY: // // iPhone PNG support: // -// By default we convert iphone-formatted PNGs back to RGB, even though -// they are internally encoded differently. You can disable this conversion -// by calling stbi_convert_iphone_png_to_rgb(0), in which case -// you will always just get the native iphone "format" through (which -// is BGR stored in RGB). +// We optionally support converting iPhone-formatted PNGs (which store +// premultiplied BGRA) back to RGB, even though they're internally encoded +// differently. To enable this conversion, call +// stbi_convert_iphone_png_to_rgb(1). // // Call stbi_set_unpremultiply_on_load(1) as well to force a divide per // pixel to remove any premultiplied alpha *only* if the image file explicitly @@ -489,6 +517,8 @@ STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); // as above, but only applies to images loaded on the thread that calls the function // this function is only available if your compiler supports thread-local variables; // calling it will fail to link if your compiler doesn't +STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply); +STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert); STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip); // ZLIB client - used by PNG, available for other purposes @@ -634,7 +664,7 @@ typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; #ifdef STBI_HAS_LROTL #define stbi_lrot(x,y) _lrotl(x,y) #else - #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) + #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (-(y) & 31))) #endif #if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) @@ -748,9 +778,12 @@ static int stbi__sse2_available(void) #ifdef STBI_NEON #include -// assume GCC or Clang on ARM targets +#ifdef _MSC_VER +#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name +#else #define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) #endif +#endif #ifndef STBI_SIMD_ALIGN #define STBI_SIMD_ALIGN(type, name) type name @@ -924,6 +957,7 @@ static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); static int stbi__pnm_test(stbi__context *s); static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__pnm_is16(stbi__context *s); #endif static @@ -998,7 +1032,7 @@ static int stbi__mad3sizes_valid(int a, int b, int c, int add) } // returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow -#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM) static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) { return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && @@ -1021,7 +1055,7 @@ static void *stbi__malloc_mad3(int a, int b, int c, int add) return stbi__malloc(a*b*c + add); } -#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM) static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) { if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; @@ -1087,9 +1121,8 @@ static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int re ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order ri->num_channels = 0; - #ifndef STBI_NO_JPEG - if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); - #endif + // test the formats with a very explicit header first (at least a FOURCC + // or distinctive magic number first) #ifndef STBI_NO_PNG if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri); #endif @@ -1107,6 +1140,13 @@ static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int re #ifndef STBI_NO_PIC if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); #endif + + // then the formats that can end up attempting to load with just 1 or 2 + // bytes matching expectations; these are prone to false positives, so + // try them later + #ifndef STBI_NO_JPEG + if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); + #endif #ifndef STBI_NO_PNM if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri); #endif @@ -1262,12 +1302,12 @@ static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, in #ifndef STBI_NO_STDIO -#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) STBI_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); #endif -#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) { return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); @@ -1277,16 +1317,16 @@ STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wch static FILE *stbi__fopen(char const *filename, char const *mode) { FILE *f; -#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) wchar_t wMode[64]; wchar_t wFilename[1024]; - if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename))) + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)/sizeof(*wFilename))) return 0; - if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode))) + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)/sizeof(*wMode))) return 0; -#if _MSC_VER >= 1400 +#if defined(_MSC_VER) && _MSC_VER >= 1400 if (0 != _wfopen_s(&f, wFilename, wMode)) f = 0; #else @@ -1662,7 +1702,8 @@ static int stbi__get16le(stbi__context *s) static stbi__uint32 stbi__get32le(stbi__context *s) { stbi__uint32 z = stbi__get16le(s); - return z + (stbi__get16le(s) << 16); + z += (stbi__uint32)stbi__get16le(s) << 16; + return z; } #endif @@ -2090,13 +2131,12 @@ stbi_inline static int stbi__extend_receive(stbi__jpeg *j, int n) int sgn; if (j->code_bits < n) stbi__grow_buffer_unsafe(j); - sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB + sgn = j->code_buffer >> 31; // sign bit always in MSB; 0 if MSB clear (positive), 1 if MSB set (negative) k = stbi_lrot(j->code_buffer, n); - if (n < 0 || n >= (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask))) return 0; j->code_buffer = k & ~stbi__bmask[n]; k &= stbi__bmask[n]; j->code_bits -= n; - return k + (stbi__jbias[n] & ~sgn); + return k + (stbi__jbias[n] & (sgn - 1)); } // get some unsigned bits @@ -2146,7 +2186,7 @@ static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); t = stbi__jpeg_huff_decode(j, hdc); - if (t < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + if (t < 0 || t > 15) return stbi__err("bad huffman code","Corrupt JPEG"); // 0 all the ac values now so we can do it 32-bits at a time memset(data,0,64*sizeof(data[0])); @@ -2203,12 +2243,12 @@ static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__ // first scan for DC coefficient, must be first memset(data,0,64*sizeof(data[0])); // 0 all the ac values now t = stbi__jpeg_huff_decode(j, hdc); - if (t == -1) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + if (t < 0 || t > 15) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); diff = t ? stbi__extend_receive(j, t) : 0; dc = j->img_comp[b].dc_pred + diff; j->img_comp[b].dc_pred = dc; - data[0] = (short) (dc << j->succ_low); + data[0] = (short) (dc * (1 << j->succ_low)); } else { // refinement scan for DC coefficient if (stbi__jpeg_get_bit(j)) @@ -2245,7 +2285,7 @@ static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__ j->code_buffer <<= s; j->code_bits -= s; zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) ((r >> 8) << shift); + data[zig] = (short) ((r >> 8) * (1 << shift)); } else { int rs = stbi__jpeg_huff_decode(j, hac); if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); @@ -2263,7 +2303,7 @@ static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__ } else { k += r; zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) (stbi__extend_receive(j,s) << shift); + data[zig] = (short) (stbi__extend_receive(j,s) * (1 << shift)); } } } while (k <= j->spec_end); @@ -3227,6 +3267,13 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan) if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; } + // check that plane subsampling factors are integer ratios; our resamplers can't deal with fractional ratios + // and I've never seen a non-corrupted JPEG file actually use them + for (i=0; i < s->img_n; ++i) { + if (h_max % z->img_comp[i].h != 0) return stbi__err("bad H","Corrupt JPEG"); + if (v_max % z->img_comp[i].v != 0) return stbi__err("bad V","Corrupt JPEG"); + } + // compute interleaved mcu info z->img_h_max = h_max; z->img_v_max = v_max; @@ -3782,6 +3829,10 @@ static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp else decode_n = z->s->img_n; + // nothing to do if no components requested; check this now to avoid + // accessing uninitialized coutput[0] later + if (decode_n <= 0) { stbi__cleanup_jpeg(z); return NULL; } + // resample and color-convert { int k; @@ -3924,6 +3975,7 @@ static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int re { unsigned char* result; stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); + if (!j) return stbi__errpuc("outofmem", "Out of memory"); STBI_NOTUSED(ri); j->s = s; stbi__setup_jpeg(j); @@ -3936,6 +3988,7 @@ static int stbi__jpeg_test(stbi__context *s) { int r; stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); + if (!j) return stbi__err("outofmem", "Out of memory"); j->s = s; stbi__setup_jpeg(j); r = stbi__decode_jpeg_header(j, STBI__SCAN_type); @@ -3960,6 +4013,7 @@ static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) { int result; stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); + if (!j) return stbi__err("outofmem", "Out of memory"); j->s = s; result = stbi__jpeg_info_raw(j, x, y, comp); STBI_FREE(j); @@ -3979,6 +4033,7 @@ static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) // fast-way is faster to check than jpeg huffman, but slow way is slower #define STBI__ZFAST_BITS 9 // accelerate all cases in default tables #define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) +#define STBI__ZNSYMS 288 // number of symbols in literal/length alphabet // zlib-style huffman encoding // (jpegs packs from left, zlib from right, so can't share code) @@ -3988,8 +4043,8 @@ typedef struct stbi__uint16 firstcode[16]; int maxcode[17]; stbi__uint16 firstsymbol[16]; - stbi_uc size[288]; - stbi__uint16 value[288]; + stbi_uc size[STBI__ZNSYMS]; + stbi__uint16 value[STBI__ZNSYMS]; } stbi__zhuffman; stbi_inline static int stbi__bitreverse16(int n) @@ -4120,7 +4175,7 @@ static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) if (s >= 16) return -1; // invalid code! // code size is s, so: b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; - if (b >= sizeof (z->size)) return -1; // some data was corrupt somewhere! + if (b >= STBI__ZNSYMS) return -1; // some data was corrupt somewhere! if (z->size[b] != s) return -1; // was originally an assert, but report failure instead. a->code_buffer >>= s; a->num_bits -= s; @@ -4317,7 +4372,7 @@ static int stbi__parse_zlib_header(stbi__zbuf *a) return 1; } -static const stbi_uc stbi__zdefault_length[288] = +static const stbi_uc stbi__zdefault_length[STBI__ZNSYMS] = { 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, @@ -4363,7 +4418,7 @@ static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) } else { if (type == 1) { // use fixed code lengths - if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0; + if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , STBI__ZNSYMS)) return 0; if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; } else { if (!stbi__compute_huffman_codes(a)) return 0; @@ -4759,6 +4814,7 @@ static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint3 // de-interlacing final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); + if (!final) return stbi__err("outofmem", "Out of memory"); for (p=0; p < 7; ++p) { int xorig[] = { 0,4,0,2,0,1,0 }; int yorig[] = { 0,0,4,0,2,0,1 }; @@ -4879,19 +4935,46 @@ static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int return 1; } -static int stbi__unpremultiply_on_load = 0; -static int stbi__de_iphone_flag = 0; +static int stbi__unpremultiply_on_load_global = 0; +static int stbi__de_iphone_flag_global = 0; STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) { - stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply; + stbi__unpremultiply_on_load_global = flag_true_if_should_unpremultiply; } STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) { - stbi__de_iphone_flag = flag_true_if_should_convert; + stbi__de_iphone_flag_global = flag_true_if_should_convert; +} + +#ifndef STBI_THREAD_LOCAL +#define stbi__unpremultiply_on_load stbi__unpremultiply_on_load_global +#define stbi__de_iphone_flag stbi__de_iphone_flag_global +#else +static STBI_THREAD_LOCAL int stbi__unpremultiply_on_load_local, stbi__unpremultiply_on_load_set; +static STBI_THREAD_LOCAL int stbi__de_iphone_flag_local, stbi__de_iphone_flag_set; + +STBIDEF void stbi__unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply) +{ + stbi__unpremultiply_on_load_local = flag_true_if_should_unpremultiply; + stbi__unpremultiply_on_load_set = 1; +} + +STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert) +{ + stbi__de_iphone_flag_local = flag_true_if_should_convert; + stbi__de_iphone_flag_set = 1; } +#define stbi__unpremultiply_on_load (stbi__unpremultiply_on_load_set \ + ? stbi__unpremultiply_on_load_local \ + : stbi__unpremultiply_on_load_global) +#define stbi__de_iphone_flag (stbi__de_iphone_flag_set \ + ? stbi__de_iphone_flag_local \ + : stbi__de_iphone_flag_global) +#endif // STBI_THREAD_LOCAL + static void stbi__de_iphone(stbi__png *z) { stbi__context *s = z->s; @@ -5272,6 +5355,32 @@ typedef struct int extra_read; } stbi__bmp_data; +static int stbi__bmp_set_mask_defaults(stbi__bmp_data *info, int compress) +{ + // BI_BITFIELDS specifies masks explicitly, don't override + if (compress == 3) + return 1; + + if (compress == 0) { + if (info->bpp == 16) { + info->mr = 31u << 10; + info->mg = 31u << 5; + info->mb = 31u << 0; + } else if (info->bpp == 32) { + info->mr = 0xffu << 16; + info->mg = 0xffu << 8; + info->mb = 0xffu << 0; + info->ma = 0xffu << 24; + info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 + } else { + // otherwise, use defaults, which is all-0 + info->mr = info->mg = info->mb = info->ma = 0; + } + return 1; + } + return 0; // error +} + static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) { int hsz; @@ -5299,6 +5408,8 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) if (hsz != 12) { int compress = stbi__get32le(s); if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); + if (compress >= 4) return stbi__errpuc("BMP JPEG/PNG", "BMP type not supported: unsupported compression"); // this includes PNG/JPEG modes + if (compress == 3 && info->bpp != 16 && info->bpp != 32) return stbi__errpuc("bad BMP", "bad BMP"); // bitfields requires 16 or 32 bits/pixel stbi__get32le(s); // discard sizeof stbi__get32le(s); // discard hres stbi__get32le(s); // discard vres @@ -5313,17 +5424,7 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) } if (info->bpp == 16 || info->bpp == 32) { if (compress == 0) { - if (info->bpp == 32) { - info->mr = 0xffu << 16; - info->mg = 0xffu << 8; - info->mb = 0xffu << 0; - info->ma = 0xffu << 24; - info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 - } else { - info->mr = 31u << 10; - info->mg = 31u << 5; - info->mb = 31u << 0; - } + stbi__bmp_set_mask_defaults(info, compress); } else if (compress == 3) { info->mr = stbi__get32le(s); info->mg = stbi__get32le(s); @@ -5338,6 +5439,7 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) return stbi__errpuc("bad BMP", "bad BMP"); } } else { + // V4/V5 header int i; if (hsz != 108 && hsz != 124) return stbi__errpuc("bad BMP", "bad BMP"); @@ -5345,6 +5447,8 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) info->mg = stbi__get32le(s); info->mb = stbi__get32le(s); info->ma = stbi__get32le(s); + if (compress != 3) // override mr/mg/mb unless in BI_BITFIELDS mode, as per docs + stbi__bmp_set_mask_defaults(info, compress); stbi__get32le(s); // discard color space for (i=0; i < 12; ++i) stbi__get32le(s); // discard color space parameters @@ -5394,8 +5498,7 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req psize = (info.offset - info.extra_read - info.hsz) >> 2; } if (psize == 0) { - STBI_ASSERT(info.offset == s->callback_already_read + (int) (s->img_buffer - s->img_buffer_original)); - if (info.offset != s->callback_already_read + (s->img_buffer - s->buffer_start)) { + if (info.offset != s->callback_already_read + (s->img_buffer - s->img_buffer_original)) { return stbi__errpuc("bad offset", "Corrupt BMP"); } } @@ -6342,6 +6445,7 @@ static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_c // intermediate buffer is RGBA result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0); + if (!result) return stbi__errpuc("outofmem", "Out of memory"); memset(result, 0xff, x*y*4); if (!stbi__pic_load_core(s,x,y,comp, result)) { @@ -6457,6 +6561,7 @@ static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_in static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) { stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); + if (!g) return stbi__err("outofmem", "Out of memory"); if (!stbi__gif_header(s, g, comp, 1)) { STBI_FREE(g); stbi__rewind( s ); @@ -6766,6 +6871,17 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i } } +static void *stbi__load_gif_main_outofmem(stbi__gif *g, stbi_uc *out, int **delays) +{ + STBI_FREE(g->out); + STBI_FREE(g->history); + STBI_FREE(g->background); + + if (out) STBI_FREE(out); + if (delays && *delays) STBI_FREE(*delays); + return stbi__errpuc("outofmem", "Out of memory"); +} + static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) { if (stbi__gif_test(s)) { @@ -6777,6 +6893,10 @@ static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int stride; int out_size = 0; int delays_size = 0; + + STBI_NOTUSED(out_size); + STBI_NOTUSED(delays_size); + memset(&g, 0, sizeof(g)); if (delays) { *delays = 0; @@ -6794,26 +6914,29 @@ static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, if (out) { void *tmp = (stbi_uc*) STBI_REALLOC_SIZED( out, out_size, layers * stride ); - if (NULL == tmp) { - STBI_FREE(g.out); - STBI_FREE(g.history); - STBI_FREE(g.background); - return stbi__errpuc("outofmem", "Out of memory"); - } + if (!tmp) + return stbi__load_gif_main_outofmem(&g, out, delays); else { out = (stbi_uc*) tmp; out_size = layers * stride; } if (delays) { - *delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers ); + int *new_delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers ); + if (!new_delays) + return stbi__load_gif_main_outofmem(&g, out, delays); + *delays = new_delays; delays_size = layers * sizeof(int); } } else { out = (stbi_uc*)stbi__malloc( layers * stride ); + if (!out) + return stbi__load_gif_main_outofmem(&g, out, delays); out_size = layers * stride; if (delays) { *delays = (int*) stbi__malloc( layers * sizeof(int) ); + if (!*delays) + return stbi__load_gif_main_outofmem(&g, out, delays); delays_size = layers * sizeof(int); } } @@ -7138,9 +7261,10 @@ static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) info.all_a = 255; p = stbi__bmp_parse_header(s, &info); - stbi__rewind( s ); - if (p == NULL) + if (p == NULL) { + stbi__rewind( s ); return 0; + } if (x) *x = s->img_x; if (y) *y = s->img_y; if (comp) { @@ -7206,8 +7330,8 @@ static int stbi__psd_is16(stbi__context *s) stbi__rewind( s ); return 0; } - (void) stbi__get32be(s); - (void) stbi__get32be(s); + STBI_NOTUSED(stbi__get32be(s)); + STBI_NOTUSED(stbi__get32be(s)); depth = stbi__get16be(s); if (depth != 16) { stbi__rewind( s ); @@ -7286,7 +7410,6 @@ static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) // Known limitations: // Does not support comments in the header section // Does not support ASCII image data (formats P2 and P3) -// Does not support 16-bit-per-channel #ifndef STBI_NO_PNM @@ -7307,7 +7430,8 @@ static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req stbi_uc *out; STBI_NOTUSED(ri); - if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n)) + ri->bits_per_channel = stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n); + if (ri->bits_per_channel == 0) return 0; if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); @@ -7317,12 +7441,12 @@ static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req *y = s->img_y; if (comp) *comp = s->img_n; - if (!stbi__mad3sizes_valid(s->img_n, s->img_x, s->img_y, 0)) + if (!stbi__mad4sizes_valid(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0)) return stbi__errpuc("too large", "PNM too large"); - out = (stbi_uc *) stbi__malloc_mad3(s->img_n, s->img_x, s->img_y, 0); + out = (stbi_uc *) stbi__malloc_mad4(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0); if (!out) return stbi__errpuc("outofmem", "Out of memory"); - stbi__getn(s, out, s->img_n * s->img_x * s->img_y); + stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8)); if (req_comp && req_comp != s->img_n) { out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); @@ -7398,11 +7522,19 @@ static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) stbi__pnm_skip_whitespace(s, &c); maxv = stbi__pnm_getinteger(s, &c); // read max value - - if (maxv > 255) - return stbi__err("max value > 255", "PPM image not 8-bit"); + if (maxv > 65535) + return stbi__err("max value > 65535", "PPM image supports only 8-bit and 16-bit images"); + else if (maxv > 255) + return 16; else - return 1; + return 8; +} + +static int stbi__pnm_is16(stbi__context *s) +{ + if (stbi__pnm_info(s, NULL, NULL, NULL) == 16) + return 1; + return 0; } #endif @@ -7458,6 +7590,9 @@ static int stbi__is_16_main(stbi__context *s) if (stbi__psd_is16(s)) return 1; #endif + #ifndef STBI_NO_PNM + if (stbi__pnm_is16(s)) return 1; + #endif return 0; } diff --git a/apps/common/external/stb_image/stb_image_write.h b/apps/common/external/stb_image/stb_image_write.h index 95943eb607..e4b32ed1bc 100644 --- a/apps/common/external/stb_image/stb_image_write.h +++ b/apps/common/external/stb_image/stb_image_write.h @@ -1,4 +1,4 @@ -/* stb_image_write - v1.15 - public domain - http://nothings.org/stb +/* stb_image_write - v1.16 - public domain - http://nothings.org/stb writes out PNG/BMP/TGA/JPEG/HDR images to C stdio - Sean Barrett 2010-2015 no warranty implied; use at your own risk @@ -140,6 +140,7 @@ Ivan Tikhonov github:ignotion Adam Schackart + Andrew Kensler LICENSE @@ -166,9 +167,9 @@ LICENSE #endif #ifndef STB_IMAGE_WRITE_STATIC // C++ forbids static forward declarations -extern int stbi_write_tga_with_rle; -extern int stbi_write_png_compression_level; -extern int stbi_write_force_png_filter; +STBIWDEF int stbi_write_tga_with_rle; +STBIWDEF int stbi_write_png_compression_level; +STBIWDEF int stbi_write_force_png_filter; #endif #ifndef STBI_WRITE_NO_STDIO @@ -178,7 +179,7 @@ STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality); -#ifdef STBI_WINDOWS_UTF8 +#ifdef STBIW_WINDOWS_UTF8 STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); #endif #endif @@ -285,7 +286,7 @@ static void stbi__stdio_write(void *context, void *data, int size) fwrite(data,1,size,(FILE*) context); } -#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) +#if defined(_WIN32) && defined(STBIW_WINDOWS_UTF8) #ifdef __cplusplus #define STBIW_EXTERN extern "C" #else @@ -296,25 +297,25 @@ STBIW_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned in STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) { - return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); + return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); } #endif static FILE *stbiw__fopen(char const *filename, char const *mode) { FILE *f; -#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) +#if defined(_WIN32) && defined(STBIW_WINDOWS_UTF8) wchar_t wMode[64]; wchar_t wFilename[1024]; - if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename))) + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)/sizeof(*wFilename))) return 0; - if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode))) + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)/sizeof(*wMode))) return 0; -#if _MSC_VER >= 1400 - if (0 != _wfopen_s(&f, wFilename, wMode)) - f = 0; +#if defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != _wfopen_s(&f, wFilename, wMode)) + f = 0; #else f = _wfopen(wFilename, wMode); #endif @@ -397,7 +398,7 @@ static void stbiw__putc(stbi__write_context *s, unsigned char c) static void stbiw__write1(stbi__write_context *s, unsigned char a) { - if (s->buf_used + 1 > sizeof(s->buffer)) + if ((size_t)s->buf_used + 1 > sizeof(s->buffer)) stbiw__write_flush(s); s->buffer[s->buf_used++] = a; } @@ -405,7 +406,7 @@ static void stbiw__write1(stbi__write_context *s, unsigned char a) static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c) { int n; - if (s->buf_used + 3 > sizeof(s->buffer)) + if ((size_t)s->buf_used + 3 > sizeof(s->buffer)) stbiw__write_flush(s); n = s->buf_used; s->buf_used = n+3; @@ -490,11 +491,22 @@ static int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data) { - int pad = (-x*3) & 3; - return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad, - "11 4 22 4" "4 44 22 444444", - 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header - 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header + if (comp != 4) { + // write RGB bitmap + int pad = (-x*3) & 3; + return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad, + "11 4 22 4" "4 44 22 444444", + 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header + 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header + } else { + // RGBA bitmaps need a v4 header + // use BI_BITFIELDS mode with 32bpp and alpha mask + // (straight BI_RGB with alpha mask doesn't work in most readers) + return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *)data,1,0, + "11 4 22 4" "4 44 22 444444 4444 4 444 444 444 444", + 'B', 'M', 14+108+x*y*4, 0, 0, 14+108, // file header + 108, x,y, 1,32, 3,0,0,0,0,0, 0xff0000,0xff00,0xff,0xff000000u, 0, 0,0,0, 0,0,0, 0,0,0, 0,0,0); // bitmap V4 header + } } STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) @@ -622,6 +634,8 @@ STBIWDEF int stbi_write_tga(char const *filename, int x, int y, int comp, const #define stbiw__max(a, b) ((a) > (b) ? (a) : (b)) +#ifndef STBI_WRITE_NO_STDIO + static void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) { int exponent; @@ -756,7 +770,7 @@ static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, f char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n"; s->func(s->context, header, sizeof(header)-1); -#ifdef __STDC_WANT_SECURE_LIB__ +#ifdef __STDC_LIB_EXT1__ len = sprintf_s(buffer, sizeof(buffer), "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); #else len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); @@ -777,7 +791,6 @@ STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, return stbi_write_hdr_core(&s, x, y, comp, (float *) data); } -#ifndef STBI_WRITE_NO_STDIO STBIWDEF int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) { stbi__write_context s = { 0 }; @@ -968,6 +981,23 @@ STBIWDEF unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, i (void) stbiw__sbfree(hash_table[i]); STBIW_FREE(hash_table); + // store uncompressed instead if compression was worse + if (stbiw__sbn(out) > data_len + 2 + ((data_len+32766)/32767)*5) { + stbiw__sbn(out) = 2; // truncate to DEFLATE 32K window and FLEVEL = 1 + for (j = 0; j < data_len;) { + int blocklen = data_len - j; + if (blocklen > 32767) blocklen = 32767; + stbiw__sbpush(out, data_len - j == blocklen); // BFINAL = ?, BTYPE = 0 -- no compression + stbiw__sbpush(out, STBIW_UCHAR(blocklen)); // LEN + stbiw__sbpush(out, STBIW_UCHAR(blocklen >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(~blocklen)); // NLEN + stbiw__sbpush(out, STBIW_UCHAR(~blocklen >> 8)); + memcpy(out+stbiw__sbn(out), data+j, blocklen); + stbiw__sbn(out) += blocklen; + j += blocklen; + } + } + { // compute adler32 on input unsigned int s1=1, s2=0; @@ -1598,6 +1628,10 @@ STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const #endif // STB_IMAGE_WRITE_IMPLEMENTATION /* Revision history + 1.16 (2021-07-11) + make Deflate code emit uncompressed blocks when it would otherwise expand + support writing BMPs with alpha channel + 1.15 (2020-07-13) unknown 1.14 (2020-02-02) updated JPEG writer to downsample chroma channels 1.13 1.12 @@ -1635,7 +1669,7 @@ STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const add HDR output fix monochrome BMP 0.95 (2014-08-17) - add monochrome TGA output + add monochrome TGA output 0.94 (2014-05-31) rename private functions to avoid conflicts with stb_image.h 0.93 (2014-05-27) diff --git a/cmake/compiler/ispc.cmake b/cmake/compiler/ispc.cmake index 77ba4c8ca2..9d8eef671a 100644 --- a/cmake/compiler/ispc.cmake +++ b/cmake/compiler/ispc.cmake @@ -2,7 +2,7 @@ ## SPDX-License-Identifier: Apache-2.0 # ISPC versions to look for, in descending order (newest first) -set(ISPC_VERSION_WORKING "1.16.1" "1.16.0") +set(ISPC_VERSION_WORKING "1.17.0" "1.16.1" "1.16.0") list(GET ISPC_VERSION_WORKING -1 ISPC_VERSION_REQUIRED) if (NOT ISPC_EXECUTABLE) diff --git a/cmake/ospray_options.cmake b/cmake/ospray_options.cmake index e2dde0fb2b..529cce9a8a 100644 --- a/cmake/ospray_options.cmake +++ b/cmake/ospray_options.cmake @@ -1,4 +1,4 @@ -## Copyright 2009-2021 Intel Corporation +## Copyright 2009-2022 Intel Corporation ## SPDX-License-Identifier: Apache-2.0 ############################################################## @@ -11,9 +11,9 @@ include(CMakeDependentOption) set(OSPRAY_CMAKECONFIG_DIR "${CMAKE_INSTALL_LIBDIR}/cmake/ospray-${OSPRAY_VERSION}") -set(RKCOMMON_VERSION_REQUIRED 1.8.0) +set(RKCOMMON_VERSION_REQUIRED 1.9.0) set(EMBREE_VERSION_REQUIRED 3.13.1) -set(OPENVKL_VERSION_REQUIRED 1.0.1) +set(OPENVKL_VERSION_REQUIRED 1.2.0) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) diff --git a/doc/api.md b/doc/api.md index e9d9d75fcf..0f6d2ef314 100644 --- a/doc/api.md +++ b/doc/api.md @@ -575,7 +575,6 @@ have a stride between voxels, specified through the supported, additional strides between scanlines (2D, `byteStride2`) and slices (3D, `byteStride3`) are not. - The parameters understood by structured volumes are summarized in the table below. @@ -589,6 +588,10 @@ table below. in object-space OSPData data the actual voxel 3D [data] + + bool cellCentered false whether the data is provided + per cell (as opposed to per + vertex) int filter `OSP_VOLUME_FILTER_TRILINEAR` filter used for reconstructing the field, @@ -609,7 +612,10 @@ table below. The size of the volume is inferred from the size of the 3D array `data`, as is the type of the voxel values (currently supported are: `OSP_UCHAR`, `OSP_SHORT`, `OSP_USHORT`, `OSP_HALF`, `OSP_FLOAT`, and -`OSP_DOUBLE`). +`OSP_DOUBLE`). Data can be provided either per cell or per vertex (the +default), selectable via the `cellCentered` parameter (which will also +affect the computed bounding box). + ### Structured Spherical Volume diff --git a/doc/compilation.md b/doc/compilation.md index 3b362c788a..882807a5bd 100644 --- a/doc/compilation.md +++ b/doc/compilation.md @@ -27,7 +27,7 @@ before you can build OSPRay you need the following prerequisites: for ISPC in the `PATH` and in the directory right "next to" the checked-out OSPRay sources.^[For example, if OSPRay is in `~/Projects/ospray`, ISPC will also be searched in - `~/Projects/ispc-v1.16.1-linux`.] Alternatively set the CMake + `~/Projects/ispc-v1.17.0-linux`.] Alternatively set the CMake variable `ISPC_EXECUTABLE` to the location of the ISPC compiler. - OSPRay builds on top of the [Intel oneAPI Rendering Toolkit common library (rkcommon)](https://www.github.com/ospray/rkcommon). The @@ -44,7 +44,7 @@ before you can build OSPRay you need the following prerequisites: or newer is required. If Embree is not found by CMake its location can be hinted with the variable `embree_DIR`. - OSPRay also heavily uses Intel [Open VKL](https://www.openvkl.org/), - installing version 1.0.1 or newer is required. If Open VKL is not + installing version 1.2.0 or newer is required. If Open VKL is not found by CMake its location can be hinted with the variable `openvkl_DIR`. - OSPRay also provides an optional module implementing the `denoiser` diff --git a/scripts/superbuild/CMakeLists.txt b/scripts/superbuild/CMakeLists.txt index 37e020bcb2..8ba036c980 100644 --- a/scripts/superbuild/CMakeLists.txt +++ b/scripts/superbuild/CMakeLists.txt @@ -1,4 +1,4 @@ -## Copyright 2009-2021 Intel Corporation +## Copyright 2009-2022 Intel Corporation ## SPDX-License-Identifier: Apache-2.0 ## Global settings ## @@ -68,42 +68,42 @@ option(ALWAYS_REBUILD "Force every project to always be rebuilt?" OFF) option(DOWNLOAD_ISPC "Download ISPC or use the one found in the system environment?" ON) if (DOWNLOAD_ISPC) - set(ISPC_VERSION "1.16.1" CACHE STRING "Which version of ISPC to download?") + set(ISPC_VERSION "1.17.0" CACHE STRING "Which version of ISPC to download?") mark_as_advanced(CLEAR ISPC_VERSION) - if (ISPC_VERSION STREQUAL "1.16.1") + if (ISPC_VERSION STREQUAL "1.17.0") if (APPLE) - set(ISPC_HASH "7dbce602d97227a9603aabfae6dc3b3aa24d1cd44f0ccfb5ae47ecd4d68e988e") + set(ISPC_HASH "e7fdcdbd5c272955249148c452ccd7295d7cf77b35ca1dec377e72b49c847bff") elseif (WIN32) - set(ISPC_HASH "b34de2c36aff2afaa56b669ea41f9e614a045564ca74fc0b138e17ccea4880b7") + set(ISPC_HASH "e9a7cc98f69357482985bcbf69fa006632cee7b3606069b4d5e16dc62092d660") else() - set(ISPC_HASH "88db3d0461147c10ed81053a561ec87d3e14265227c03318f4fcaaadc831037f") + set(ISPC_HASH "6acc5df75efdce437f79b1b6489be8567c6d009e19dcc4851b9b37012afce1f7") endif() endif() else() mark_as_advanced(FORCE ISPC_VERSION) endif() -set(RKCOMMON_VERSION "devel" CACHE STRING "Which version of rkcommon to build?") -if (RKCOMMON_VERSION STREQUAL "1.8.0") - set(RKCOMMON_HASH "d9593de25a9b705d7d56f53d655c71901a25be21d88aa493d39d2d6e61ed7393") +set(RKCOMMON_VERSION "1.9.0" CACHE STRING "Which version of rkcommon to build?") +if (RKCOMMON_VERSION STREQUAL "1.9.0") + set(RKCOMMON_HASH "54ef58226b5f2b6eb80091d806b94d563ce748a6c8d04db7836c20945fd99929") endif() option(DOWNLOAD_TBB "Download TBB or use the one found in the system environment?" ON) if (DOWNLOAD_TBB) - set(TBB_VERSION "2021.4.0" CACHE STRING "Which version of TBB to download?") + set(TBB_VERSION "2021.5.0" CACHE STRING "Which version of TBB to download?") mark_as_advanced(CLEAR TBB_VERSION) option(BUILD_TBB_FROM_SOURCE "Build TBB from source or use pre-built version?" OFF) mark_as_advanced(CLEAR BUILD_TBB_FROM_SOURCE) - if (TBB_VERSION STREQUAL "2021.4.0") + if (TBB_VERSION STREQUAL "2021.5.0") if (BUILD_TBB_FROM_SOURCE) - set(TBB_HASH "08ade531be2e4e904eb6bec8e01da51eb3b0e4e86738128eb2722b95e3fcb5e6") + set(TBB_HASH "83ea786c964a384dd72534f9854b419716f412f9d43c0be88d41874763e7bb47") else() if (APPLE) - set(TBB_HASH "519d599939c41808d6b9170253b4c993b40d481f2c56b1a3cf390aea37ddb355") + set(TBB_HASH "388c1c25314e3251e38c87ade2323af74cdaae2aec9b68e4c206d61c30ef9c33") elseif (WIN32) - set(TBB_HASH "3868c557739a7a5b74c985571648c066167fd7a0a8c63bdac00a6cfeeb58037f") + set(TBB_HASH "096c004c7079af89fe990bb259d58983b0ee272afa3a7ef0733875bfe09fcd8e") else() - set(TBB_HASH "f1d26b9f3741e5d573050eef2902fcd739d2913c990f4c879a310e543fc5ffd5") + set(TBB_HASH "74861b1586d6936b620cdab6775175de46ad8b0b36fa6438135ecfb8fb5bdf98") endif() endif() endif() @@ -113,39 +113,39 @@ else() endif() option(BUILD_EMBREE_FROM_SOURCE "Build Embree or use pre-built version?" ON) -set(EMBREE_VERSION "3.13.2" CACHE STRING "Which version of Embree to build?") -if (EMBREE_VERSION STREQUAL "3.13.2") +set(EMBREE_VERSION "3.13.3" CACHE STRING "Which version of Embree to build?") +if (EMBREE_VERSION STREQUAL "3.13.3") if (BUILD_EMBREE_FROM_SOURCE) - set(EMBREE_HASH "eaa7a8ecd78594fb9eed75b2abbabd30dd68afb49556c250799daaeec016237c") + set(EMBREE_HASH "e98db1522d377a1d47c04ff7db29ccb4207412751c33cb756de32b20ae66e9b1") else() if (APPLE) - set(EMBREE_HASH "6e9442e516cd54c2e7f6454c90fb8cda5721d76a14d29880ffa387820a486762") + set(EMBREE_HASH "79f2e56b4697f833be9b5088bea9c7ce22119adacaf09390e23a7140f6383fd9") elseif (WIN32) - set(EMBREE_HASH "76570583a3d3e78f74b3cde2b0bbff8b0cc527959cc68b9e501b295e3aa7a960") + set(EMBREE_HASH "1decef1ab8d0f5b2b4ef59bf70b303da05d0fe8d741d0e15bacaccd144055821") else() - set(EMBREE_HASH "8142c1fa0e8e89e279581e873f558b008a3d49b9b1e0091393e50377bcc52639") + set(EMBREE_HASH "0c5884a6d3dffa082c329327276b91cc686ae0374e91a4c2cbea8cf6f7ddfeb4") endif() endif() endif() -set(OPENVKL_VERSION "1.1.0" CACHE STRING "Which version of OpenVKL to build?") -if (OPENVKL_VERSION STREQUAL "1.1.0") - set(OPENVKL_HASH "2fd751f5eaa37342bd4574d3fdc0cf82e36b67646442a71c3ebe3aa859ccbc13") +set(OPENVKL_VERSION "1.2.0" CACHE STRING "Which version of OpenVKL to build?") +if (OPENVKL_VERSION STREQUAL "1.2.0") + set(OPENVKL_HASH "5a3ae6cf4fc1379d326d929e91d3a95409d6e79773544ca12a02e28476ad905b") endif() option(BUILD_OIDN "Build OpenImageDenoise as a part of the superbuild." OFF) if (BUILD_OIDN) - set(OIDN_VERSION "1.4.2" CACHE STRING "Which version of OpenImageDenoise to build?") + set(OIDN_VERSION "1.4.3" CACHE STRING "Which version of OpenImageDenoise to build?") mark_as_advanced(CLEAR OIDN_VERSION) option(BUILD_OIDN_FROM_SOURCE "Build OpenImageDenoise or use pre-built version?" OFF) mark_as_advanced(CLEAR BUILD_OIDN_FROM_SOURCE) - if (OIDN_VERSION STREQUAL "1.4.2") + if (OIDN_VERSION STREQUAL "1.4.3") if (APPLE) - set(OIDN_HASH "176df94914d3bf920987f71650de05d6335f058fd631d2a5caa0e4e65f8e4fab") + set(OIDN_HASH "3dffa9a9a0f2cb9a0d89cf42ee7e3acb0db5552f0128d7868af71e9b80d72ecf") elseif (WIN32) - set(OIDN_HASH "5d688d60b2abb3bc2aa19a357ef2347747635c08b3fc7fb95e0abee9a2202e70") + set(OIDN_HASH "5c0f3d8a4211dfd064f7baeb4403e33cba36035ad8b6c8a8c0cabb7c705db4f4") else() - set(OIDN_HASH "2d0eb873b79935166321df7197659e09ad5fc8a3aca3865793d12bb2b58f608f") + set(OIDN_HASH "f6ec3b996c69386bbc69f61bc0e6fd17765530e6aa788cfc7a5d3e739548cd58") endif() endif() else() diff --git a/scripts/superbuild/dependencies/dep_benchmark.cmake b/scripts/superbuild/dependencies/dep_benchmark.cmake index 9010621328..779dda6b13 100644 --- a/scripts/superbuild/dependencies/dep_benchmark.cmake +++ b/scripts/superbuild/dependencies/dep_benchmark.cmake @@ -1,4 +1,4 @@ -## Copyright 2021 Intel Corporation +## Copyright 2021-2022 Intel Corporation ## SPDX-License-Identifier: Apache-2.0 set(COMPONENT_NAME benchmark) @@ -10,8 +10,8 @@ else() endif() ExternalProject_Add(${COMPONENT_NAME} - URL "https://github.com/google/benchmark/archive/refs/tags/v1.5.5.zip" - URL_HASH "SHA256=30f2e5156de241789d772dd8b130c1cb5d33473cc2f29e4008eab680df7bd1f0" + URL "https://github.com/google/benchmark/archive/refs/tags/v1.6.1.zip" + URL_HASH "SHA256=367e963b8620080aff8c831e24751852cffd1f74ea40f25d9cc1b667a9dd5e45" # Skip updating on subsequent builds (faster) UPDATE_COMMAND ""