diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..3dae8a8421 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.rc text working-tree-encoding=UTF-16LE-BOM eol=CRLF diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index fd5a532186..2c4c62fa06 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -56,12 +56,13 @@ variables: .job_template: &test_job_unix <<: *test_job script: - - scripts/tests/run_tests.sh + - LD_LIBRARY_PATH="$CI_PROJECT_DIR/build/install/lib:$LD_LIBRARY_PATH" PATH="$CI_PROJECT_DIR/build/install/bin:$PATH" scripts/tests/run_tests.sh "$CI_PROJECT_DIR" .job_template: &test_job_windows <<: *test_job script: - - scripts\tests\run_tests.ps1 + - $env:Path += ";$CI_PROJECT_DIR\build\install\bin" + - scripts\tests\run_tests.ps1 "$CI_PROJECT_DIR" ### BUILD JOBS ### @@ -110,6 +111,16 @@ build-ubuntu1804-gcc: - build/install expire_in: 3 day +build-centos8: + <<: *build_job_docker + image: ospray/docker-images:centos8 + script: + - scripts/build_gitlab/linux.sh -G Ninja + artifacts: + paths: + - build/install + expire_in: 3 day + build-centos7-icc: <<: *build_job_docker_modules script: @@ -136,6 +147,14 @@ build-centos7-gcc: - build/install expire_in: 3 day +build-centos7-mpi: + <<: *build_job_docker_modules + script: + - module load cmake + - module load intel + - scripts/build_gitlab/linux.sh -DBUILD_OSPRAY_MODULE_MPI=ON + allow_failure: true + build-debug: <<: *build_job_docker script: @@ -201,14 +220,11 @@ build-scan-kw: source-protex-scan: stage: build - image: ospray-protex-centos7 + image: amd64/openjdk:8 script: - - /tmp/source-scan-protex.sh - dependencies: - tags: - - protex-scan-docker - ## Temporarily allow to fail - when: manual + - scripts/build_gitlab/source-scan-protex.sh + dependencies: [] + tags: [docker] allow_failure: true @@ -220,19 +236,21 @@ test-centos7-gcc: dependencies: - build-centos7-gcc +test-centos8: + <<: *test_job_unix + image: ospray/docker-images:centos8 + dependencies: + - build-centos8 + test-ubuntu1804-gcc: <<: *test_job_unix image: ospray/docker-images:ubuntu18.04 - script: - - LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/lib64/ scripts/tests/run_tests.sh dependencies: - build-ubuntu1804-gcc test-ubuntu1604-gcc: <<: *test_job_unix image: ospray/docker-images:ubuntu16.04 - script: - - LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/lib64/ scripts/tests/run_tests.sh dependencies: - build-ubuntu1604-gcc @@ -286,12 +304,12 @@ generate-ci-baseline: <<: *test_job_unix image: ospray/docker-images:ubuntu18.04 script: - - scripts/tests/generate_baseline.sh + - LD_LIBRARY_PATH="$CI_PROJECT_DIR/build/install/lib:$LD_LIBRARY_PATH" PATH="$CI_PROJECT_DIR/build/install/bin:$PATH" scripts/tests/generate_baseline.sh dependencies: - build-ubuntu1804-gcc artifacts: paths: - - img + - generated_test_images expire_in: 3 day when: manual @@ -329,7 +347,7 @@ web: ### RELEASE JOBS ### -release-linux-icc: +release-linux: <<: *release_job image: ospray/docker-images:centos7-mod script: diff --git a/CHANGELOG.md b/CHANGELOG.md index a364d16ca7..fffdeaf6e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,27 +1,65 @@ Version History --------------- +### Changes in v2.1.0: + +- New clipping geometries feature that allows clipping any scene + (geometry and volumes); all OSPRay geometry types can by used as + clipping geometry + - Inverted clipping is supported via new `invertNormals` parameter + of `GeometricModel` + - Currently there is a fixed upper limit (64) of how many clipping + geometries can be nested + - When clipping with curves geometry (any basis except linear) + some rendering artifacts may appear +- New plane geometry defined via plane equation and optional bounding + box +- Sun-sky light based on physical model of Hošek-Wilkie +- Support for photometric lights (e.g. IES or EULUMDAT) +- Add new `ospGetTaskDuration` API call to query execution time of + asynchronous tasks +- Support for 16bit (unsigned short) textures +- Add static `cpp::Device::current` method as a C++ wrapper equivalent + to `ospGetCurrentDevice` +- Generalized `cpp::Device` parameter setting to match other handle + types +- Passing `NULL` to `ospRelease` is not reported as error anymore +- Fix computation of strides for `OSPData` +- Fix transparency in `scivis` renderer +- Add missing C++ wrapper for `ospGetVariance` +- Proper demonstration of `ospGetVariance` in `ospTutorialAsync` +- Fix handling of `--osp:device-params` to process and set all passed + arguments first before committing the device, to ensure it is + committed in a valid state. +- Object factory functions are now registered during module + initialization via the appropriate `registerType` function +- Fix issue with OSPRay ignoring tasking system thread count settings +- Fix issue where OSPRay always loaded the ISPC module, even if not + required +- OSPRay now requires minimum Open VKL v0.9.0 + ### Changes in v2.0.1: -- Fix bug where Embree user-defined geometries were not indexed correctly - in the scene, which now requires Embree v3.8.0+ -- Fix crash when the path tracer encounters geometric models that do not - have a material -- Fix crash when some path tracer materials generated NULL bsdfs +- Fix bug where Embree user-defined geometries were not indexed + correctly in the scene, which now requires Embree v3.8.0+ +- Fix crash when the path tracer encounters geometric models that do + not have a material +- Fix crash when some path tracer materials generated `NULL` bsdfs - Fix bug where `ospGetBounds` returned incorrect values - Fix missing symbol in denoiser module - Fix missing symbol exports on Windows for all OSPRay built modules - Add the option to specify a single color for geometric models -- The `scivis` renderer now respects the opacity component of `color` on - geometric models -- Fix various inconsistent handling of frame buffer alpha between renderers +- The `scivis` renderer now respects the opacity component of `color` + on geometric models +- Fix various inconsistent handling of frame buffer alpha between + renderers - `ospGetCurrentDevice` now increments the ref count of the returned - `OSPDevice` handle, so applications will need to release the handle when - finished by using `ospDeviceRelease` accordingly + `OSPDevice` handle, so applications will need to release the handle + when finished by using `ospDeviceRelease` accordingly - Added denoiser to `ospExamples` app - Added `module_mpi` to superbuild (disabled by default) -- The superbuild now will emit a CMake error when using any 32-bit CMake - generator, as 32-bit builds are not supported +- The superbuild now will emit a CMake error when using any 32-bit + CMake generator, as 32-bit builds are not supported ### Changes in v2.0.0: diff --git a/LICENSE.txt b/LICENSE.txt index 8a9931e725..68c771a099 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -174,60 +174,3 @@ incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -------------------------------------------------------------------------------- - -Copyright 2008, Google Inc. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md index 9df8a1d749..b7f4da448d 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ OSPRay ====== -This is release v2.0.1 of Intel® OSPRay. For changes and new features +This is release v2.1.0 of Intel® OSPRay. For changes and new features see the [changelog](CHANGELOG.md). Visit http://www.ospray.org for more information. @@ -101,7 +101,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 0.8.0 or newer is required. If Open VKL is not + installing version 0.9.0 or newer is required. If Open VKL is not found by CMake its location can be hinted with the variable `openvkl_DIR`. @@ -157,6 +157,7 @@ Run with: mkdir build cd build cmake [/scripts/superbuild] +cmake --build . ``` On Windows make sure to select the non-default 64bit generator, e.g. @@ -185,10 +186,10 @@ 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. +: set to OFF will download a pre-built version of Open Image Denoise. BUILD\_OIDN\_VERSION -: determines which verison of OpenImageDenoise to pull down. +: determines which version of Open Image Denoise to pull down. For the full set of options, run: @@ -476,7 +477,7 @@ to avoid leaking the underlying device object. OSPRay allows applications to query runtime properties of a device in order to do enhanced validation of what device was loaded at runtime. The following function can be used to get these device-specific -properties (attiributes about the device, not paramter values) +properties (attributes about the device, not parameter values) ``` {.cpp} int64_t ospDeviceGetProperty(OSPDevice, OSPDeviceProperty); @@ -718,13 +719,13 @@ is to created a shared data array, which is done with ``` {.cpp} OSPData ospNewSharedData(const void *sharedData, - OSPDataType, - uint64_t numItems1, - int64_t byteStride1 = 0, - uint64_t numItems2 = 1, - int64_t byteStride2 = 0, - uint64_t numItems3 = 1, - int64_t byteStride3 = 0); + OSPDataType, + uint64_t numItems1, + int64_t byteStride1 = 0, + uint64_t numItems2 = 1, + int64_t byteStride2 = 0, + uint64_t numItems3 = 1, + int64_t byteStride3 = 0); ``` The call returns an `OSPData` handle to the created array. The calling @@ -738,70 +739,74 @@ also be negative. If `byteStride` is zero it will be determined automatically (e.g., as `sizeof(type)`). Strides do not need to be ordered, i.e., `byteStride2` can be smaller than `byteStride1`, which is equivalent to a transpose. However, if the stride should be calculated, -then an ordering like `byteStride1 < byteStride2` is assumed to -disambiguate. +then an ordering in dimensions is assumed to disambiguate, i.e., +`byteStride1 < byteStride2 < byteStride3`. The enum type `OSPDataType` describes the different element types that can be represented in OSPRay; valid constants are listed in the table below. -| Type/Name | Description | -|:------------------------|:------------------------------------------------------------------| -| OSP\_DEVICE | API device object reference | -| OSP\_DATA | data reference | -| OSP\_OBJECT | generic object reference | -| OSP\_CAMERA | camera object reference | -| OSP\_FRAMEBUFFER | framebuffer object reference | -| OSP\_LIGHT | light object reference | -| OSP\_MATERIAL | material object reference | -| OSP\_TEXTURE | texture object reference | -| OSP\_RENDERER | renderer object reference | -| OSP\_WORLD | world object reference | -| OSP\_GEOMETRY | geometry object reference | -| OSP\_VOLUME | volume object reference | -| OSP\_TRANSFER\_FUNCTION | transfer function object reference | -| OSP\_IMAGE\_OPERATION | image operation object reference | -| OSP\_STRING | C-style zero-terminated character string | -| OSP\_CHAR | 8 bit signed character scalar | -| OSP\_UCHAR | 8 bit unsigned character scalar | -| OSP\_VEC\[234\]UC | … and \[234\]-element vector | -| OSP\_USHORT | 16 bit unsigned integer scalar | -| OSP\_INT | 32 bit signed integer scalar | -| OSP\_VEC\[234\]I | … and \[234\]-element vector | -| OSP\_UINT | 32 bit unsigned integer scalar | -| OSP\_VEC\[234\]UI | … and \[234\]-element vector | -| OSP\_LONG | 64 bit signed integer scalar | -| OSP\_VEC\[234\]L | … and \[234\]-element vector | -| OSP\_ULONG | 64 bit unsigned integer scalar | -| OSP\_VEC\[234\]UL | … and \[234\]-element vector | -| OSP\_FLOAT | 32 bit single precision floating-point scalar | -| OSP\_VEC\[234\]F | … and \[234\]-element vector | -| OSP\_DOUBLE | 64 bit double precision floating-point scalar | -| OSP\_BOX\[1234\]I | 32 bit integer box (lower + upper bounds) | -| OSP\_BOX\[1234\]F | 32 bit single precision floating-point box (lower + upper bounds) | -| OSP\_LINEAR\[234\]F | 32 bit single precision floating-point linear transform | -| OSP\_AFFINE\[234\]F | 32 bit single precision floating-point affine transform | -| OSP\_VOID\_PTR | raw memory address (only found in module extensions) | +| Type/Name | Description | +|:------------------------|:--------------------------------------------------------------------------------------------| +| OSP\_DEVICE | API device object reference | +| OSP\_DATA | data reference | +| OSP\_OBJECT | generic object reference | +| OSP\_CAMERA | camera object reference | +| OSP\_FRAMEBUFFER | framebuffer object reference | +| OSP\_LIGHT | light object reference | +| OSP\_MATERIAL | material object reference | +| OSP\_TEXTURE | texture object reference | +| OSP\_RENDERER | renderer object reference | +| OSP\_WORLD | world object reference | +| OSP\_GEOMETRY | geometry object reference | +| OSP\_VOLUME | volume object reference | +| OSP\_TRANSFER\_FUNCTION | transfer function object reference | +| OSP\_IMAGE\_OPERATION | image operation object reference | +| OSP\_STRING | C-style zero-terminated character string | +| OSP\_CHAR | 8 bit signed character scalar | +| OSP\_UCHAR | 8 bit unsigned character scalar | +| OSP\_VEC\[234\]UC | … and \[234\]-element vector | +| OSP\_USHORT | 16 bit unsigned integer scalar | +| OSP\_VEC\[234\]US | … and \[234\]-element vector | +| OSP\_INT | 32 bit signed integer scalar | +| OSP\_VEC\[234\]I | … and \[234\]-element vector | +| OSP\_UINT | 32 bit unsigned integer scalar | +| OSP\_VEC\[234\]UI | … and \[234\]-element vector | +| OSP\_LONG | 64 bit signed integer scalar | +| OSP\_VEC\[234\]L | … and \[234\]-element vector | +| OSP\_ULONG | 64 bit unsigned integer scalar | +| OSP\_VEC\[234\]UL | … and \[234\]-element vector | +| OSP\_FLOAT | 32 bit single precision floating-point scalar | +| OSP\_VEC\[234\]F | … and \[234\]-element vector | +| OSP\_DOUBLE | 64 bit double precision floating-point scalar | +| OSP\_BOX\[1234\]I | 32 bit integer box (lower + upper bounds) | +| OSP\_BOX\[1234\]F | 32 bit single precision floating-point box (lower + upper bounds) | +| OSP\_LINEAR\[23\]F | 32 bit single precision floating-point linear transform (\[23\] vectors) | +| OSP\_AFFINE\[23\]F | 32 bit single precision floating-point affine transform (linear transform plus translation) | +| OSP\_VOID\_PTR | raw memory address (only found in module extensions) | : Valid named constants for `OSPDataType`. +If the elements of the array are handles to objects, then their +reference counter is incremented. + An opaque `OSPData` with memory allocated by OSPRay is created with ``` {.cpp} OSPData ospNewData(OSPDataType, - uint32_t numItems1, - uint32_t numItems2 = 1, - uint32_t numItems3 = 1); + uint32_t numItems1, + uint32_t numItems2 = 1, + uint32_t numItems3 = 1); ``` To allow for (partial) copies or updates of data arrays use ``` {.cpp} void ospCopyData(const OSPData source, - OSPData destination, - uint32_t destinationIndex1 = 0, - uint32_t destinationIndex2 = 0, - uint32_t destinationIndex3 = 0); + OSPData destination, + uint32_t destinationIndex1 = 0, + uint32_t destinationIndex2 = 0, + uint32_t destinationIndex3 = 0); ``` which will copy the whole[^3] content of the `source` array into @@ -817,7 +822,7 @@ shared with OSPData by the application (created with - the source array must be shared as well (thus `ospCopyData` cannot be used to read opaque data) -- if source and destination memory overlaps (aliasing), then behaviour +- if source and destination memory overlaps (aliasing), then behavior is undefined - except if source and destination regions are identical (including matching strides), which can be used by application to mark that @@ -1014,7 +1019,7 @@ the vertices and data values. Vertex ordering is the same as vertex. To maintain VTK data compatibility an index array may be specified via -the `indexPrefixed` array that allow vertex indices to be interleaved +the `indexPrefixed` array that allows vertex indices to be interleaved with cell sizes in the following format: $n, id_1, ..., id_n, m, id_1, ..., id_m$. @@ -1032,7 +1037,7 @@ $n, id_1, ..., id_n, m, id_1, ..., id_m$. | | | | `OSP_WEDGE` | | | | | `OSP_PYRAMID` | | bool | hexIterative | false | hexahedron interpolation method, defaults to fast non-iterative version which could have rendering inaccuracies may appear if hex is not parallelepiped | -| bool | precomputedNormals | true | whether to accelerate by precomputing, at a cost of 12 bytes/face | +| bool | precomputedNormals | false | whether to accelerate by precomputing, at a cost of 12 bytes/face | : Additional configuration parameters for unstructured volumes. @@ -1062,6 +1067,8 @@ and opacities. It is create by passing the string “`piecewiseLinear`” to : Parameters accepted by the linear transfer function. +The arrays `color` and `opacity` can be of different length. + ### VolumetricModels Volumes in OSPRay are given volume rendering appearance information @@ -1097,7 +1104,7 @@ maximum of 2^32^ primitives. ### Mesh -A mesh consiting of either triangles or quads is created by calling +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: @@ -1127,25 +1134,25 @@ A mesh consisting of subdivision surfaces, created by specifying a geometry of type “`subdivision`”. Once created, a subdivision recognizes the following parameters: -| Type | Name | Default| Description | -|:----------|:--------------------|----------------------------------:|:-------------------------------------------------------------------------------| -| vec3f\[\] | vertex.position | NULL| [data](#data) array of vertex positions | -| vec4f\[\] | vertex.color | NULL| [data](#data) array of vertex colors (RGBA) | -| vec2f\[\] | vertex.texcoord | NULL| [data](#data) array of vertex texture coordinates | -| float | level | | 5 global level of tessellation, default is 5 | -| uint\[\] | index | NULL| [data](#data) array of indices (into the vertex array(s)) | -| float\[\] | index.level | NULL| [data](#data) array of per-edge levels of tessellation, overrides global level | -| uint\[\] | face | NULL| [data](#data) array holding the number of indices/edges (3 to 15) per face | -| vec2i\[\] | edgeCrease.index | NULL| [data](#data) array of edge crease indices | -| float\[\] | edgeCrease.weight | NULL| [data](#data) array of edge crease weights | -| uint\[\] | vertexCrease.index | NULL| [data](#data) array of vertex crease indices | -| float\[\] | vertexCrease.weight | NULL| [data](#data) array of vertex crease weights | -| int | mode | `OSP_SUBDIVISION_SMOOTH_BOUNDARY`| subdivision edge boundary mode. Supported modes are: | -| | | | `OSP_SUBDIVISION_NO_BOUNDARY` | -| | | | `OSP_SUBDIVISION_SMOOTH_BOUNDARY` | -| | | | `OSP_SUBDIVISION_PIN_CORNERS` | -| | | | `OSP_SUBDIVISION_PIN_BOUNDARY` | -| | | | `OSP_SUBDIVISION_PIN_ALL` | +| Type | Name | Description | +|:----------|:--------------------|:----------------------------------------------------------------------------------------------------------------------| +| vec3f\[\] | vertex.position | [data](#data) array of vertex positions | +| vec4f\[\] | vertex.color | optional [data](#data) array of vertex colors (RGBA) | +| vec2f\[\] | vertex.texcoord | optional [data](#data) array of vertex texture coordinates | +| float | level | global level of tessellation, default 5 | +| uint\[\] | index | [data](#data) array of indices (into the vertex array(s)) | +| float\[\] | index.level | optional [data](#data) array of per-edge levels of tessellation, overrides global level | +| uint\[\] | face | optional [data](#data) array holding the number of indices/edges (3 to 15) per face, defaults to 4 (a pure quad mesh) | +| vec2i\[\] | edgeCrease.index | optional [data](#data) array of edge crease indices | +| float\[\] | edgeCrease.weight | optional [data](#data) array of edge crease weights | +| uint\[\] | vertexCrease.index | optional [data](#data) array of vertex crease indices | +| float\[\] | vertexCrease.weight | optional [data](#data) array of vertex crease weights | +| int | mode | subdivision edge boundary mode, supported modes are: | +| | | `OSP_SUBDIVISION_NO_BOUNDARY` | +| | | `OSP_SUBDIVISION_SMOOTH_BOUNDARY` (default) | +| | | `OSP_SUBDIVISION_PIN_CORNERS` | +| | | `OSP_SUBDIVISION_PIN_BOUNDARY` | +| | | `OSP_SUBDIVISION_PIN_ALL` | : Parameters defining a Subdivision geometry. @@ -1214,8 +1221,8 @@ discussion of curve types and data formatting). If a constant `radius` is used and positions are specified in a `vec3f[]` type of `vertex.position` format, then type/basis defaults to `OSP_ROUND` and `OSP_LINEAR` (this is the fastest and most memory -efficient mode). Implementation is with round linear segements where -each segment corresponds to a link between two vertices. +efficient mode). Implementation is with round linear segments where each +segment corresponds to a link between two vertices. The following section describes the properties of different curve basis’ and how they use the data provided in data buffers: @@ -1238,7 +1245,7 @@ OSP\_BSPLINE the vertex buffer. This basis is not interpolating, thus the curve does in general not go through any of the control points directly. Using this basis, 3 control points can be shared for two continuous - neighboring curve segments, e.g. the curves $(p0, p1, p2, p3)$ and + neighboring curve segments, e.g., the curves $(p0, p1, p2, p3)$ and $(p1, p2, p3, p4)$ are C1 continuous. This feature make this basis a good choice to construct continuous multi-segment curves, as memory consumption can be kept minimal. @@ -1291,6 +1298,21 @@ by calling `ospNewGeometry` with type string “`box`”. : Parameters defining a boxes geometry. +### Planes + +OSPRay can directly render planes defined by plane equation coefficients +in its implicit form $ax + by + cz + d = 0$. By default planes are +infinite but their extents can be limited by defining optional bounding +boxes. A planes geometry can be created by calling `ospNewGeometry` with +type string “`plane`”. + +| Type | Name Descript | ion | +|:----------|:-------------------|:---------------------------------------------------------| +| vec4f\[\] | plane.coefficients | [data](#data) array of plane coefficients $(a, b, c, d)$ | +| box3f\[\] | plane.bounds | optional [data](#data) array of bounding boxes | + +: Parameters defining a planes geometry. + ### Isosurfaces OSPRay can directly render multiple isosurfaces of a volume without @@ -1299,11 +1321,11 @@ calling `ospNewGeometry` with type string “`isosurface`”. Each isosurface will be colored according to the [transfer function](#transfer-function) assigned to the `volume`. -| Type | Name | Description | -|:-------------------|:---------|:----------------------------------------------------------------------| -| float | isovalue | single isovalues | -| float\[\] | isovalue | [data](#data) array of isovalues | -| OSPVolumetricModel | volume | handle of the [VolumetricModels](#volumetricmodels) to be isosurfaced | +| Type | Name | Description | +|:-------------------|:---------|:---------------------------------------------------------------------| +| float | isovalue | single isovalues | +| float\[\] | isovalue | [data](#data) array of isovalues | +| OSPVolumetricModel | volume | handle of the [VolumetricModel](#volumetricmodels) to be isosurfaced | : Parameters defining an isosurfaces geometry. @@ -1320,20 +1342,27 @@ OSPGeometricModel ospNewGeometricModel(OSPGeometry geometry); Color and material are fetched with the primitive ID of the hit (clamped to the valid range, thus a single color or material is fine), or mapped -first via the `index` array (if present). All paramters are optional, +first via the `index` array (if present). All parameters are optional, however, some renderers (notably the [path tracer](#path-tracer)) require a material to be set. Materials are either handles of `OSPMaterial`, or indices into the `material` array on the [renderer](#renderers), which allows to build a [world](#world) which can be used by different types of renderers. -| Type | Name | Description | -|:-----------------------------|:---------|:----------------------------------------------------------------------------------------------------------------------------------------------------| -| 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 | -| 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 | -| uint8\[\] | index | optional [data](#data) array of per-primitive indices into `color` and `material` | +An `invertNormals` flag allows to invert (shading) normal vectors of the +rendered geometry. That is particularly useful for clipping. By changing +normal vectors orientation one can control whether inside or outside of +the clipping geometry is being removed. For example, a clipping geometry +with normals oriented outside clips everything what’s inside. + +| Type | Name | Description | +|:-----------------------------|:--------------|:----------------------------------------------------------------------------------------------------------------------------------------------------| +| 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 | +| 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 | +| uint8\[\] | index | optional [data](#data) array of per-primitive indices into `color` and `material` | +| bool | invertNormals | inverts all shading normals (Ns), default false | : Parameters understood by GeometricModel. @@ -1399,20 +1428,22 @@ 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 +### Spotlight / Photometric Light The spotlight is a light emitting into a cone of directions. It is created by passing the type string “`spot`” to `ospNewLight`. 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, in world-space | -| 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` | +| Type | Name | Default| Description | +|:----------|:----------------------|------------:|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| vec3f | position | $(0, 0, 0)$| the center of the spotlight, in world-space | +| 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\[\] | 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. @@ -1423,6 +1454,21 @@ 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)). +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`. + +![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 @@ -1480,6 +1526,28 @@ and `intensity`](#lights)). It is created by passing the type string Note that the [SciVis renderer](#scivis-renderer) uses ambient lights to control the color and intensity of the computed ambient occlusion (AO). +### Sun-Sky Light + +The sun-sky light is a combination of a `distant` light for the sun and +a procedural `hdri` light for the sky. It is created by passing the type +string “`sunSky`” to `ospNewLight`. The sun-sky light surrounds the +scene and illuminates it from infinity and can be used for rendering +outdoor scenes. The radiance values are calculated using the +Hošek-Wilkie sky model and solar radiance function. In addition to the +[general parameters](#lights) the following special parameters are +supported: + +| Type | Name | Default| Description | +|:------|:----------|-------------:|:----------------------------------------------------| +| vec3f | up | $(0, 1, 0)$| zenith of sky in world-space | +| vec3f | direction | $(0, -1, 0)$| main emission direction of the sun | +| float | turbidity | 3| atmospheric turbidity due to particles, in \[1–10\] | +| float | albedo | 0.3| ground reflectance, in \[0–1\] | + +: Special parameters accepted by the `sunSky` light. + +The lowest elevation for the sun is restricted to the horizon. + ### Emissive Objects The [path tracer](#path-tracer) will consider illumination by @@ -1499,17 +1567,34 @@ create a group call OSPGroup ospNewGroup(); ``` -Groups take arrays of geometric models and volumetric models, but they -are optional. In other words, there is no need to create empty arrays if -there are no geometries or volumes in the group. - -| Type | Name | Default| Description | -|:-----------------------|:-------------|--------:|:----------------------------------------------------------------------------------------------------------------------------------------------------------------| -| OSPGeometricModel\[\] | geometry | NULL| [data](#data) array of [GeometricModels](#geometricmodels) | -| OSPVolumetricModel\[\] | volume | NULL| [data](#data) array of [VolumetricModels](#volumetricmodels) | -| bool | dynamicScene | false| use RTC\_SCENE\_DYNAMIC flag (faster BVH build, slower ray traversal), otherwise uses RTC\_SCENE\_STATIC flag (faster ray traversal, slightly slower BVH build) | -| bool | compactMode | false| tell Embree to use a more compact BVH in memory by trading ray traversal performance | -| bool | robustMode | false| tell Embree to enable more robust ray intersection code paths (slightly slower) | +Groups take arrays of geometric models, volumetric models and clipping +geometric models, but they are optional. In other words, there is no +need to create empty arrays if there are no geometries or volumes in the +group. + +By adding `OSPGeometricModel`s to the `clippingGeometry` array a +clipping geometry feature is enabled. Geometries assigned to this +parameter will be used as clipping geometries. Any supported geometry +can be used for clipping. The only requirement is that it has to +distinctly partition space into clipping and non-clipping one. These +include: spheres, boxes, infinite planes, closed meshes, closed +subdivisions and curves. All geometries and volumes assigned to +`geometry` or `volume` will be clipped. Use of clipping geometry that is +not closed (or infinite) will result in rendering artifacts. User can +decide which part of space is clipped by changing shading normals +orientation with the `invertNormals` flag of the +[GeometricModel](#geometricmodels). When more than single clipping +geometry is defined all clipping areas will be “added” together – an +union of these areas will be applied. + +| Type | Name | Default| Description | +|:-----------------------|:-----------------|--------:|:----------------------------------------------------------------------------------------------------------------------------------------------------------------| +| OSPGeometricModel\[\] | geometry | NULL| [data](#data) array of [GeometricModels](#geometricmodels) | +| OSPVolumetricModel\[\] | volume | NULL| [data](#data) array of [VolumetricModels](#volumetricmodels) | +| OSPGeometricModel\[\] | clippingGeometry | NULL| [data](#data) array of [GeometricModels](#geometricmodels) used for clipping | +| bool | dynamicScene | false| use RTC\_SCENE\_DYNAMIC flag (faster BVH build, slower ray traversal), otherwise uses RTC\_SCENE\_STATIC flag (faster ray traversal, slightly slower BVH build) | +| bool | compactMode | false| tell Embree to use a more compact BVH in memory by trading ray traversal performance | +| bool | robustMode | false| tell Embree to enable more robust ray intersection code paths (slightly slower) | : Parameters understood by groups. @@ -1554,6 +1639,15 @@ world has been committed. To get this information, call OSPBounds ospGetBounds(OSPObject); ``` +The result is returned in the provided `OSPBounds`[^6] struct: + +``` {.cpp} +typedef struct { + float lower[3]; + float upper[3]; +} OSPBounds; +``` + This call can also take `OSPGroup` and `OSPInstance` as well: all other object types will return an empty bounding box. @@ -1589,8 +1683,8 @@ General parameters of all renderers are | int | maxPathLength | 20| maximum ray recursion depth | | float | minContribution | 0.001| sample contributions below this value will be neglected to speedup rendering | | float | varianceThreshold | 0| threshold for adaptive accumulation | -| float / vec3f / vec4f | backgroundColor | black, transparent| background color and alpha (RGBA), if no map\_backplate is set | -| OSPTexture | map\_backplate | | optional [texture](#texture) image used as background | +| float / vec3f / vec4f | backgroundColor | black, transparent| background color and alpha (RGBA), if no `map_backplate` is set | +| OSPTexture | map\_backplate | | optional [texture](#texture) image used as background (use texture type `texture2d`) | | OSPTexture | map\_maxDepth | | optional screen-sized float [texture](#texture) with maximum far distance per pixel (use texture type `texture2d`) | | OSPMaterial\[\] | material | | optional [data](#data) array of [materials](#materials) which can be indexed by a [GeometricModel](#geometricmodels)’s `material` parameter | @@ -1643,11 +1737,11 @@ realistic materials. This renderer is created by passing the type string parameters](#renderer) understood by all renderers the path tracer supports the following special parameters: -| Type | Name | Default| Description | -|:------|:------------------|--------:|:-----------------------------------------------------------------------------------| -| bool | geometryLights | true| whether to render light emitted from geometries | -| int | roulettePathLengt | h 5| ray recursion depth at which to start Russian roulette termination | -| float | maxContribution | ∞| samples are clamped to this value before they are accumulated into the framebuffer | +| Type | Name | Default| Description | +|:------|:-------------------|--------:|:------------------------------------------------------------------------------------------------| +| bool | geometryLights | true| whether geometries with an emissive material (e.g., [Luminous](#luminous)) illuminate the scene | +| int | roulettePathLength | 5| ray recursion depth at which to start Russian roulette termination | +| float | maxContribution | ∞| samples are clamped to this value before they are accumulated into the framebuffer | : Special parameters understood by the path tracer. @@ -1727,7 +1821,7 @@ with `Tf`. Normal mapping can simulate small geometric features via the texture `map_Bump`. The normals $n$ in the normal map are with respect to the local tangential shading coordinate system and are encoded as $½(n+1)$, -thus a texel $(0.5, 0.5, 1)$[^6] represents the unperturbed shading +thus a texel $(0.5, 0.5, 1)$[^7] represents the unperturbed shading normal $(0, 0, 1)$. Because of this encoding an sRGB gamma [texture](#texture) format is ignored and normals are always fetched as linear from a normal map. Note that the orientation of normal maps is @@ -2042,9 +2136,9 @@ average, thus individual flakes are not visible. The [path tracer](#path-tracer) supports the Luminous material which emits light uniformly in all directions and which can thus be used to -turn any geometric object into a light source. It is created by passing -the type string “`luminous`” to `ospNewMaterial`. The amount of constant -radiance that is emitted is determined by combining the general +turn any geometric object into a light source[^8]. It is created by +passing the type string “`luminous`” to `ospNewMaterial`. The amount of +constant radiance that is emitted is determined by combining the general parameters of lights: [`color` and `intensity`](#lights). | Type | Name | Default| Description | @@ -2088,19 +2182,23 @@ its parameters are as follows The supported texture formats for `texture2d` are: -| Name | Description | -|:----------------------|:------------------------------------------------------------| -| OSP\_TEXTURE\_RGBA8 | 8 bit \[0–255\] linear components red, green, blue, alpha | -| OSP\_TEXTURE\_SRGBA | 8 bit sRGB gamma encoded color components, and linear alpha | -| OSP\_TEXTURE\_RGBA32F | 32 bit float components red, green, blue, alpha | -| OSP\_TEXTURE\_RGB8 | 8 bit \[0–255\] linear components red, green, blue | -| OSP\_TEXTURE\_SRGB | 8 bit sRGB gamma encoded components red, green, blue | -| OSP\_TEXTURE\_RGB32F | 32 bit float components red, green, blue | -| OSP\_TEXTURE\_R8 | 8 bit \[0–255\] linear single component | -| OSP\_TEXTURE\_RA8 | 8 bit \[0–255\] linear two component | -| OSP\_TEXTURE\_L8 | 8 bit \[0–255\] gamma encoded luminance | -| OSP\_TEXTURE\_LA8 | 8 bit \[0–255\] gamma encoded luminance, and linear alpha | -| OSP\_TEXTURE\_R32F | 32 bit float single component | +| Name | Description | +|:----------------------|:---------------------------------------------------------------------------| +| OSP\_TEXTURE\_RGBA8 | 8 bit \[0–255\] linear components red, green, blue, alpha | +| OSP\_TEXTURE\_SRGBA | 8 bit sRGB gamma encoded color components, and linear alpha | +| OSP\_TEXTURE\_RGBA32F | 32 bit float components red, green, blue, alpha | +| OSP\_TEXTURE\_RGB8 | 8 bit \[0–255\] linear components red, green, blue | +| OSP\_TEXTURE\_SRGB | 8 bit sRGB gamma encoded components red, green, blue | +| OSP\_TEXTURE\_RGB32F | 32 bit float components red, green, blue | +| OSP\_TEXTURE\_R8 | 8 bit \[0–255\] linear single component red | +| OSP\_TEXTURE\_RA8 | 8 bit \[0–255\] linear two components red, alpha | +| OSP\_TEXTURE\_L8 | 8 bit \[0–255\] gamma encoded luminance (replicated into red, green, blue) | +| OSP\_TEXTURE\_LA8 | 8 bit \[0–255\] gamma encoded luminance, and linear alpha | +| OSP\_TEXTURE\_R32F | 32 bit float single component red | +| OSP\_TEXTURE\_RGBA16 | 16 bit \[0–65535\] linear components red, green, blue, alpha | +| OSP\_TEXTURE\_RGB16 | 16 bit \[0–65535\] linear components red, green, blue | +| OSP\_TEXTURE\_RA16 | 16 bit \[0–65535\] linear two components red, alpha | +| OSP\_TEXTURE\_R16 | 16 bit \[0–65535\] linear single component red | : Supported texture formats by `texture2d`, i.e., valid constants of type `OSPTextureFormat`. @@ -2124,9 +2222,9 @@ transfer function) on arbitrary surfaces inside the volume (as opposed to an isosurface showing a particular value in the volume). Its parameters are as follows -| Type | Name | Description | -|:----------|:-------|:--------------------------------------| -| OSPVolume | volume | volume used to generate color lookups | +| Type | Name | Description | +|:-------------------|:-------|:--------------------------------------------------------------------| +| OSPVolumetricModel | volume | [VolumetricModel](#volumetricmodels) used to generate color lookups | : Parameters of `volume` texture type. @@ -2203,7 +2301,11 @@ supports the special parameters listed in the table below. | float | apertureRadius | size of the aperture, controls the depth of field | | float | focusDistance | distance at where the image is sharpest when depth of field is enabled | | bool | architectural | vertical edges are projected to be parallel | -| int | stereoMode | 0: no stereo (default), 1: left eye, 2: right eye, 3: side-by-side | +| int | stereoMode | `OSPStereoMode` for stereo rendering, possible values are: | +| | | `OSP_STEREO_NONE` (default) | +| | | `OSP_STEREO_LEFT` | +| | | `OSP_STEREO_RIGHT` | +| | | `OSP_STEREO_SIDE_BY_SIDE` | | float | interpupillaryDistance | distance between left and right eye when stereo is enabled | : Parameters accepted by the perspective camera. @@ -2221,7 +2323,8 @@ image. If finer control of the lens shift is needed use `imageStart` & `imageEnd`. Because the camera is now effectively leveled its image plane and thus the plane of focus is oriented parallel to the front of buildings, the whole façade appears sharp, as can be seen in the example -images below. +images below. The resolution of the [framebuffer](#framebuffer) is not +altered by `imageStart`/`imageEnd`.
Example image created with the perspective camera, featuring depth of field.
@@ -2291,11 +2394,11 @@ normalized screen-space pixel coordinates `screenPos` use ``` {.cpp} void ospPick(OSPPickResult *, - OSPFrameBuffer, - OSPRenderer, - OSPCamera, - OSPWorld, - osp_vec2f screenPos); + OSPFrameBuffer, + OSPRenderer, + OSPCamera, + OSPWorld, + osp_vec2f screenPos); ``` The result is returned in the provided `OSPPickResult` struct: @@ -2322,9 +2425,9 @@ information associated with pixels). To create a new framebuffer object of given size `size` (in pixels), color format, and channels use ``` {.cpp} -OSPFrameBuffer ospNewFrameBuffer(osp_vec2i size, - OSPFrameBufferFormat format = OSP_FB_SRGBA, - uint32_t frameBufferChannels = OSP_FB_COLOR); +OSPFrameBuffer ospNewFrameBuffer(int size_x, int size_y, + OSPFrameBufferFormat format = OSP_FB_SRGBA, + uint32_t frameBufferChannels = OSP_FB_COLOR); ``` The parameter `format` describes the format the color buffer has *on the @@ -2381,8 +2484,7 @@ The application can map the given channel of a framebuffer – and thus access the stored pixel information – via ``` {.cpp} -const void *ospMapFrameBuffer(OSPFrameBuffer, - OSPFrameBufferChannel = OSP_FB_COLOR); +const void *ospMapFrameBuffer(OSPFrameBuffer, OSPFrameBufferChannel = OSP_FB_COLOR); ``` Note that `OSP_FB_ACCUM` or `OSP_FB_VARIANCE` cannot be mapped. The @@ -2418,7 +2520,9 @@ float ospGetVariance(OSPFrameBuffer); Note this value is only updated after synchronizing with `OSP_FRAME_FINISHED`, as further described in [asynchronous -rendering](#asynchronous-rendering). +rendering](#asynchronous-rendering). The estimated variance can be used +by the application as a quality indicator and thus to decide whether to +stop or to continue progressive rendering. The framebuffer takes a list of pixel operations to be applied to the image in sequence as an `OSPData`. The pixel operations will be run in @@ -2496,12 +2600,7 @@ combining a frame buffer, renderer, camera, and world. What to render and how to render it depends on the renderer’s parameters. If the framebuffer supports accumulation (i.e., it was created with `OSP_FB_ACCUM`) then successive calls to `ospRenderFrame` -will progressively refine the rendered image. If additionally the -framebuffer has an `OSP_FB_VARIANCE` channel then `ospRenderFrame` -returns an estimate of the current variance of the rendered image, -otherwise `inf` is returned. The estimated variance can be used by the -application as a quality indicator and thus to decide whether to stop or -to continue progressive rendering. +will progressively refine the rendered image. To start an render task, use @@ -2557,6 +2656,18 @@ As the given running task runs (as tracked by the `OSPFuture`), applications can query a boolean \[0,1\] result if the passed event has been completed. +Applications can query how long an async task ran with + +``` {.cpp} +float ospGetTaskDuration(OSPFuture); +``` + +This returns the wall clock execution time of the task in seconds. If +the task is still running, this will block until the task is completed. +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 @@ -2597,7 +2708,7 @@ Tutorial -------- A minimal working example demonstrating how to use OSPRay can be found -at `apps/tutorials/ospTutorial.c`[^7]. +at `apps/tutorials/ospTutorial.c`[^9]. An example of building `ospTutorial.c` with CMake can be found in `apps/tutorials/ospTutorialFindospray/`. @@ -2664,8 +2775,8 @@ objects for the specific scene like `cpp::Geometry`, `cpp::Volume`, `cpp::Light` etc. The `detail::Builder` base struct is mostly responsible for setting up -OSPRay `world` and objects common in all scenes (for eg: lighting and -ground plane), which can be conveniently overridden in the derived +OSPRay `world` and objects common in all scenes (for example lighting +and ground plane), which can be conveniently overridden in the derived builders. Given below are different scenes listed with their string identifiers: @@ -2711,9 +2822,19 @@ unstructured\_volume This app comes with three [renderer](#renderers) options: `scivis`, `pathtracer` and `debug`. The app provides some common rendering -controls like `pixel samples` and other more specific to the renderer +controls like `pixelSamples` and other more specific to the renderer type like `aoIntensity` for `scivis` renderer. +The sun-sky lighting can be used in a sample scene by enabling the +`renderSunSky` option of the `pathtracer` renderer. It allows the user +to change `turbidity` and `sunDirection`. + +
+
Rendering an evening sky with the renderSunSky option.
+
+ + + [^1]: For example, if OSPRay is in `~/Projects/ospray`, ISPC will also be searched in `~/Projects/ispc-v1.12.0-linux` @@ -2731,8 +2852,14 @@ type like `aoIntensity` for `scivis` renderer. [^5]: actually a parallelogram -[^6]: respectively $(127, 127, 255)$ for 8 bit textures +[^6]: `OSPBounds` has essentially the same layout as the `OSP_BOX3F` + [`OSPDataType`](#data). + +[^7]: respectively $(127, 127, 255)$ for 8 bit textures and + $(32767, 32767, 65535)$ for 16 bit textures + +[^8]: If `geometryLights` is enabled in the [path tracer](#path-tracer). -[^7]: A C++ version that uses the C++ convenience wrappers of OSPRay’s +[^9]: A C++ version that uses the C++ convenience wrappers of OSPRay’s C99 API via `include/ospray/ospray_cpp.h` is available at `apps/tutorials/ospTutorial.cpp`. diff --git a/apps/common/external/stb_image/stb_image.h b/apps/common/external/stb_image/stb_image.h index efad9f6ddb..2857f05d38 100644 --- a/apps/common/external/stb_image/stb_image.h +++ b/apps/common/external/stb_image/stb_image.h @@ -1,9 +1,4 @@ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wcomma" -#pragma clang diagnostic ignored "-Wimplicit-fallthrough" -#pragma clang diagnostic ignored "-Wunused-parameter" - -/* stb_image - v2.19 - public domain image loader - http://nothings.org/stb +/* stb_image - v2.25 - public domain image loader - http://nothings.org/stb no warranty implied; use at your own risk Do this: @@ -53,6 +48,12 @@ LICENSE RECENT REVISION HISTORY: + 2.25 (2020-02-02) fix warnings + 2.24 (2020-02-02) fix warnings; thread-local failure_reason and flip_vertically + 2.23 (2019-08-11) fix clang static analysis warning + 2.22 (2019-03-04) gif fixes, fix warnings + 2.21 (2019-02-25) fix typo in comment + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs 2.19 (2018-02-11) fix warning 2.18 (2018-01-30) fix warnings 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings @@ -89,6 +90,7 @@ RECENT REVISION HISTORY: Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query) Arseny Kapoulkine John-Mark Allen + Carmelo J Fdez-Aguera Bug & warning fixes Marc LeBlanc David Woo Guillaume George Martins Mozeiko @@ -104,8 +106,9 @@ RECENT REVISION HISTORY: Aldo Culquicondor Philipp Wiesemann Dale Weiler github:sammyhw Oriol Ferrer Mesia Josh Tobin Matthew Gregan github:phprus Julian Raschke Gregory Mullen Baldur Karlsson github:poppolopoppo - Christian Floisand Kevin Schmidt github:darealshinji - Blazej Dariusz Roszkowski github:Michaelangel007 + Christian Floisand Kevin Schmidt JR Smith github:darealshinji + Brad Weinberger Matvey Cherevko github:Michaelangel007 + Blazej Dariusz Roszkowski Alexander Veselov */ #ifndef STBI_INCLUDE_STB_IMAGE_H @@ -166,6 +169,16 @@ RECENT REVISION HISTORY: // // =========================================================================== // +// UNICODE: +// +// If compiling for Windows and you wish to use Unicode filenames, compile +// with +// #define STBI_WINDOWS_UTF8 +// and pass utf8-encoded filenames. Call stbi_convert_wchar_to_utf8 to convert +// Windows wchar_t filenames to utf8. +// +// =========================================================================== +// // Philosophy // // stb libraries are designed with the following priorities: @@ -176,12 +189,12 @@ RECENT REVISION HISTORY: // // Sometimes I let "good performance" creep up in priority over "easy to maintain", // and for best performance I may provide less-easy-to-use APIs that give higher -// performance, in addition to the easy to use ones. Nevertheless, it's important +// performance, in addition to the easy-to-use ones. Nevertheless, it's important // to keep in mind that from the standpoint of you, a client of this library, // all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all. // // Some secondary priorities arise directly from the first two, some of which -// make more explicit reasons why performance can't be emphasized. +// provide more explicit reasons why performance can't be emphasized. // // - Portable ("ease of use") // - Small source code footprint ("easy to maintain") @@ -224,11 +237,10 @@ RECENT REVISION HISTORY: // // HDR image support (disable by defining STBI_NO_HDR) // -// stb_image now supports loading HDR images in general, and currently -// the Radiance .HDR file format, although the support is provided -// generically. You can still load any file through the existing interface; -// if you attempt to load an HDR file, it will be automatically remapped to -// LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; +// stb_image supports loading HDR images in general, and currently the Radiance +// .HDR file format specifically. You can still load any file through the existing +// interface; if you attempt to load an HDR file, it will be automatically remapped +// to LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; // both of these constants can be reconfigured through this interface: // // stbi_hdr_to_ldr_gamma(2.2f); @@ -262,7 +274,7 @@ RECENT REVISION HISTORY: // // By default we convert iphone-formatted PNGs back to RGB, even though // they are internally encoded differently. You can disable this conversion -// by by calling stbi_convert_iphone_png_to_rgb(0), in which case +// 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). // @@ -324,6 +336,7 @@ enum STBI_rgb_alpha = 4 }; +#include typedef unsigned char stbi_uc; typedef unsigned short stbi_us; @@ -331,11 +344,13 @@ typedef unsigned short stbi_us; extern "C" { #endif +#ifndef STBIDEF #ifdef STB_IMAGE_STATIC #define STBIDEF static #else #define STBIDEF extern #endif +#endif ////////////////////////////////////////////////////////////////////////////// // @@ -360,10 +375,6 @@ typedef struct STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); -#ifndef STBI_NO_GIF -STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); -#endif - #ifndef STBI_NO_STDIO STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); @@ -371,6 +382,14 @@ STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in // for stbi_load_from_file, file pointer is left pointing immediately after image #endif +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +#endif + +#ifdef STBI_WINDOWS_UTF8 +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); +#endif + //////////////////////////////////// // // 16-bits-per-channel interface @@ -418,7 +437,7 @@ STBIDEF int stbi_is_hdr_from_file(FILE *f); // get a VERY brief reason for failure -// NOT THREADSAFE +// on most compilers (and ALL modern mainstream compilers) this is threadsafe STBIDEF const char *stbi_failure_reason (void); // free the loaded image -- this is just free() @@ -451,6 +470,11 @@ STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); // flip the image vertically, so the first pixel in the output array is the bottom left 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_flip_vertically_on_load_thread(int flag_true_if_should_flip); + // ZLIB client - used by PNG, available for other purposes STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); @@ -530,6 +554,12 @@ STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const ch #define STBI_ASSERT(x) assert(x) #endif +#ifdef __cplusplus +#define STBI_EXTERN extern "C" +#else +#define STBI_EXTERN extern +#endif + #ifndef _MSC_VER #ifdef __cplusplus @@ -541,6 +571,17 @@ STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const ch #define stbi_inline __forceinline #endif +#ifndef STBI_NO_THREAD_LOCALS + #if defined(__cplusplus) && __cplusplus >= 201103L + #define STBI_THREAD_LOCAL thread_local + #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L + #define STBI_THREAD_LOCAL _Thread_local + #elif defined(__GNUC__) + #define STBI_THREAD_LOCAL __thread + #elif defined(_MSC_VER) + #define STBI_THREAD_LOCAL __declspec(thread) +#endif +#endif #ifdef _MSC_VER typedef unsigned short stbi__uint16; @@ -654,14 +695,18 @@ static int stbi__cpuid3(void) #define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name +#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) static int stbi__sse2_available(void) { int info3 = stbi__cpuid3(); return ((info3 >> 26) & 1) != 0; } +#endif + #else // assume GCC-style if not VC++ #define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) +#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) static int stbi__sse2_available(void) { // If we're even attempting to compile this on GCC/Clang, that means @@ -669,6 +714,8 @@ static int stbi__sse2_available(void) // instructions at will, and so are we. return 1; } +#endif + #endif #endif @@ -845,19 +892,24 @@ static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); #endif -// this is not threadsafe -static const char *stbi__g_failure_reason; +static +#ifdef STBI_THREAD_LOCAL +STBI_THREAD_LOCAL +#endif +const char *stbi__g_failure_reason; STBIDEF const char *stbi_failure_reason(void) { return stbi__g_failure_reason; } +#ifndef STBI_NO_FAILURE_STRINGS static int stbi__err(const char *str) { stbi__g_failure_reason = str; return 0; } +#endif static void *stbi__malloc(size_t size) { @@ -896,11 +948,13 @@ static int stbi__mul2sizes_valid(int a, int b) return a <= INT_MAX/b; } +#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) // returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow static int stbi__mad2sizes_valid(int a, int b, int add) { return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); } +#endif // returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow static int stbi__mad3sizes_valid(int a, int b, int c, int add) @@ -918,12 +972,14 @@ static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) } #endif +#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) // mallocs with size overflow checking static void *stbi__malloc_mad2(int a, int b, int add) { if (!stbi__mad2sizes_valid(a, b, add)) return NULL; return stbi__malloc(a*b + add); } +#endif static void *stbi__malloc_mad3(int a, int b, int c, int add) { @@ -967,13 +1023,29 @@ static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); #endif -static int stbi__vertically_flip_on_load = 0; +static int stbi__vertically_flip_on_load_global = 0; STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) { - stbi__vertically_flip_on_load = flag_true_if_should_flip; + stbi__vertically_flip_on_load_global = flag_true_if_should_flip; } +#ifndef STBI_THREAD_LOCAL +#define stbi__vertically_flip_on_load stbi__vertically_flip_on_load_global +#else +static STBI_THREAD_LOCAL int stbi__vertically_flip_on_load_local, stbi__vertically_flip_on_load_set; + +STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load_local = flag_true_if_should_flip; + stbi__vertically_flip_on_load_set = 1; +} + +#define stbi__vertically_flip_on_load (stbi__vertically_flip_on_load_set \ + ? stbi__vertically_flip_on_load_local \ + : stbi__vertically_flip_on_load_global) +#endif // STBI_THREAD_LOCAL + static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) { memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields @@ -995,6 +1067,8 @@ static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int re #endif #ifndef STBI_NO_PSD if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); + #else + STBI_NOTUSED(bpc); #endif #ifndef STBI_NO_PIC if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); @@ -1075,6 +1149,7 @@ static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) } } +#ifndef STBI_NO_GIF static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) { int slice; @@ -1082,10 +1157,11 @@ static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int byt stbi_uc *bytes = (stbi_uc *)image; for (slice = 0; slice < z; ++slice) { - stbi__vertical_flip(bytes, w, h, bytes_per_pixel); - bytes += slice_size; + stbi__vertical_flip(bytes, w, h, bytes_per_pixel); + bytes += slice_size; } } +#endif static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) { @@ -1136,7 +1212,7 @@ static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, return (stbi__uint16 *) result; } -#if !defined(STBI_NO_HDR) || !defined(STBI_NO_LINEAR) +#if !defined(STBI_NO_HDR) && !defined(STBI_NO_LINEAR) static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) { if (stbi__vertically_flip_on_load && result != NULL) { @@ -1148,10 +1224,38 @@ 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) +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) +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); +} +#endif + static FILE *stbi__fopen(char const *filename, char const *mode) { FILE *f; -#if defined(_MSC_VER) && _MSC_VER >= 1400 +#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) + wchar_t wMode[64]; + wchar_t wFilename[1024]; + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename))) + return 0; + + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode))) + return 0; + +#if _MSC_VER >= 1400 + if (0 != _wfopen_s(&f, wFilename, wMode)) + f = 0; +#else + f = _wfopen(wFilename, wMode); +#endif + +#elif defined(_MSC_VER) && _MSC_VER >= 1400 if (0 != fopen_s(&f, filename, mode)) f=0; #else @@ -1242,15 +1346,15 @@ STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *u STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) { unsigned char *result; - stbi__context s; - stbi__start_mem(&s,buffer,len); - + stbi__context s; + stbi__start_mem(&s,buffer,len); + result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); if (stbi__vertically_flip_on_load) { - stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); + stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); } - return result; + return result; } #endif @@ -1419,6 +1523,9 @@ stbi_inline static stbi_uc stbi__get8(stbi__context *s) return 0; } +#if defined(STBI_NO_JPEG) && defined(STBI_NO_HDR) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else stbi_inline static int stbi__at_eof(stbi__context *s) { if (s->io.read) { @@ -1430,7 +1537,11 @@ stbi_inline static int stbi__at_eof(stbi__context *s) return s->img_buffer >= s->img_buffer_end; } +#endif +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) +// nothing +#else static void stbi__skip(stbi__context *s, int n) { if (n < 0) { @@ -1447,7 +1558,11 @@ static void stbi__skip(stbi__context *s, int n) } s->img_buffer += n; } +#endif +#if defined(STBI_NO_PNG) && defined(STBI_NO_TGA) && defined(STBI_NO_HDR) && defined(STBI_NO_PNM) +// nothing +#else static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) { if (s->io.read) { @@ -1471,18 +1586,27 @@ static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) } else return 0; } +#endif +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) +// nothing +#else static int stbi__get16be(stbi__context *s) { int z = stbi__get8(s); return (z << 8) + stbi__get8(s); } +#endif +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) +// nothing +#else static stbi__uint32 stbi__get32be(stbi__context *s) { stbi__uint32 z = stbi__get16be(s); return (z << 16) + stbi__get16be(s); } +#endif #if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) // nothing @@ -1504,7 +1628,9 @@ static stbi__uint32 stbi__get32le(stbi__context *s) #define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings - +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else ////////////////////////////////////////////////////////////////////////////// // // generic converter from built-in img_n to req_comp @@ -1520,7 +1646,11 @@ static stbi_uc stbi__compute_y(int r, int g, int b) { return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); } +#endif +#if defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) { int i,j; @@ -1544,18 +1674,18 @@ static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int r // convert source image with img_n components to one with req_comp components; // avoid switch per pixel, so use switch per scanline and massive macros switch (STBI__COMBO(img_n, req_comp)) { - STBI__CASE(1,2) { dest[0]=src[0], dest[1]=255; } break; + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=255; } break; STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=255; } break; STBI__CASE(2,1) { dest[0]=src[0]; } break; STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; } break; - STBI__CASE(3,4) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=255; } break; STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; - STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = 255; } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = 255; } break; STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; - STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = src[3]; } break; - STBI__CASE(4,3) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; default: STBI_ASSERT(0); } #undef STBI__CASE @@ -1564,12 +1694,20 @@ static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int r STBI_FREE(data); return good; } +#endif +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) +// nothing +#else static stbi__uint16 stbi__compute_y_16(int r, int g, int b) { return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); } +#endif +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) +// nothing +#else static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) { int i,j; @@ -1593,18 +1731,18 @@ static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int r // convert source image with img_n components to one with req_comp components; // avoid switch per pixel, so use switch per scanline and massive macros switch (STBI__COMBO(img_n, req_comp)) { - STBI__CASE(1,2) { dest[0]=src[0], dest[1]=0xffff; } break; + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=0xffff; } break; STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=0xffff; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=0xffff; } break; STBI__CASE(2,1) { dest[0]=src[0]; } break; STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; } break; - STBI__CASE(3,4) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=0xffff; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=0xffff; } break; STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; - STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]), dest[1] = 0xffff; } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = 0xffff; } break; STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; - STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]), dest[1] = src[3]; } break; - STBI__CASE(4,3) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; default: STBI_ASSERT(0); } #undef STBI__CASE @@ -1613,6 +1751,7 @@ static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int r STBI_FREE(data); return good; } +#endif #ifndef STBI_NO_LINEAR static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) @@ -1628,7 +1767,11 @@ static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) for (k=0; k < n; ++k) { output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); } - if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f; + } + if (n < comp) { + for (i=0; i < x*y; ++i) { + output[i*comp + n] = data[i*comp + n]/255.0f; + } } STBI_FREE(data); return output; @@ -3601,7 +3744,7 @@ static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp int k; unsigned int i,j; stbi_uc *output; - stbi_uc *coutput[4] = {}; + stbi_uc *coutput[4] = { NULL, NULL, NULL, NULL }; stbi__resample res_comp[4]; @@ -3722,7 +3865,7 @@ static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp if (n == 1) for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; else - for (i=0; i < z->s->img_x; ++i) *out++ = y[i], *out++ = 255; + for (i=0; i < z->s->img_x; ++i) { *out++ = y[i]; *out++ = 255; } } } } @@ -3736,9 +3879,8 @@ static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { - unsigned char* result = nullptr; + unsigned char* result; stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); - if (!j) return result; STBI_NOTUSED(ri); j->s = s; stbi__setup_jpeg(j); @@ -3751,7 +3893,6 @@ 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); @@ -3776,7 +3917,6 @@ 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); @@ -4558,7 +4698,6 @@ 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 }; @@ -4740,7 +4879,7 @@ static void stbi__de_iphone(stbi__png *z) static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) { stbi_uc palette[1024], pal_img_n=0; - stbi_uc has_trans=0, tc[3]; + stbi_uc has_trans=0, tc[3]={0}; stbi__uint16 tc16[3]; stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; int first=1,k,interlace=0, color=0, is_iphone=0; @@ -4884,6 +5023,8 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) ++s->img_n; } STBI_FREE(z->expanded); z->expanded = NULL; + // end of PNG chunk, read and skip CRC + stbi__get32be(s); return 1; } @@ -5018,11 +5159,11 @@ static int stbi__high_bit(unsigned int z) { int n=0; if (z == 0) return -1; - if (z >= 0x10000) n += 16, z >>= 16; - if (z >= 0x00100) n += 8, z >>= 8; - if (z >= 0x00010) n += 4, z >>= 4; - if (z >= 0x00004) n += 2, z >>= 2; - if (z >= 0x00002) n += 1, z >>= 1; + if (z >= 0x10000) { n += 16; z >>= 16; } + if (z >= 0x00100) { n += 8; z >>= 8; } + if (z >= 0x00010) { n += 4; z >>= 4; } + if (z >= 0x00004) { n += 2; z >>= 2; } + if (z >= 0x00002) { n += 1;/* >>= 1;*/ } return n; } @@ -5039,7 +5180,7 @@ static int stbi__bitcount(unsigned int a) // extract an arbitrarily-aligned N-bit value (N=bits) // from v, and then make it 8-bits long and fractionally // extend it to full full range. -static int stbi__shiftsigned(int v, int shift, int bits) +static int stbi__shiftsigned(unsigned int v, int shift, int bits) { static unsigned int mul_table[9] = { 0, @@ -5053,7 +5194,7 @@ static int stbi__shiftsigned(int v, int shift, int bits) v <<= -shift; else v >>= shift; - STBI_ASSERT(v >= 0 && v < 256); + STBI_ASSERT(v < 256); v >>= (8-bits); STBI_ASSERT(bits >= 0 && bits <= 8); return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits]; @@ -5063,6 +5204,7 @@ typedef struct { int bpp, offset, hsz; unsigned int mr,mg,mb,ma, all_a; + int extra_read; } stbi__bmp_data; static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) @@ -5075,6 +5217,7 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) info->offset = stbi__get32le(s); info->hsz = hsz = stbi__get32le(s); info->mr = info->mg = info->mb = info->ma = 0; + info->extra_read = 14; if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); if (hsz == 12) { @@ -5118,6 +5261,7 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) info->mr = stbi__get32le(s); info->mg = stbi__get32le(s); info->mb = stbi__get32le(s); + info->extra_read += 12; // not documented, but generated by photoshop and handled by mspaint if (info->mr == info->mg && info->mg == info->mb) { // ?!?!? @@ -5174,13 +5318,19 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req if (info.hsz == 12) { if (info.bpp < 24) - psize = (info.offset - 14 - 24) / 3; + psize = (info.offset - info.extra_read - 24) / 3; } else { if (info.bpp < 16) - psize = (info.offset - 14 - info.hsz) >> 2; + psize = (info.offset - info.extra_read - info.hsz) >> 2; + } + if (psize == 0) { + STBI_ASSERT(info.offset == (s->img_buffer - s->buffer_start)); } - s->img_n = ma ? 4 : 3; + if (info.bpp == 24 && ma == 0xff000000) + s->img_n = 3; + else + s->img_n = ma ? 4 : 3; if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 target = req_comp; else @@ -5202,7 +5352,7 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req if (info.hsz != 12) stbi__get8(s); pal[i][3] = 255; } - stbi__skip(s, info.offset - 14 - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); + stbi__skip(s, info.offset - info.extra_read - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); if (info.bpp == 1) width = (s->img_x + 7) >> 3; else if (info.bpp == 4) width = (s->img_x + 1) >> 1; else if (info.bpp == 8) width = s->img_x; @@ -5216,6 +5366,8 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req out[z++] = pal[color][0]; out[z++] = pal[color][1]; out[z++] = pal[color][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; if((--bit_offset) < 0) { bit_offset = 7; v = stbi__get8(s); @@ -5249,7 +5401,7 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; int z = 0; int easy=0; - stbi__skip(s, info.offset - 14 - info.hsz); + stbi__skip(s, info.offset - info.extra_read - info.hsz); if (info.bpp == 24) width = 3 * s->img_x; else if (info.bpp == 16) width = 2*s->img_x; else /* bpp = 32 and pad = 0 */ width=0; @@ -5308,7 +5460,7 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req stbi_uc *p1 = out + j *s->img_x*target; stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; for (i=0; i < (int) s->img_x*target; ++i) { - t = p1[i], p1[i] = p2[i], p2[i] = t; + t = p1[i]; p1[i] = p2[i]; p2[i] = t; } } } @@ -5488,6 +5640,8 @@ static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req int RLE_repeating = 0; int read_next_pixel = 1; STBI_NOTUSED(ri); + STBI_NOTUSED(tga_x_origin); // @TODO + STBI_NOTUSED(tga_y_origin); // @TODO // do a tiny bit of precessing if ( tga_image_type >= 8 ) @@ -5651,6 +5805,7 @@ static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req // Microsoft's C compilers happy... [8^( tga_palette_start = tga_palette_len = tga_palette_bits = tga_x_origin = tga_y_origin = 0; + STBI_NOTUSED(tga_palette_start); // OK, done return tga_data; } @@ -5798,7 +5953,7 @@ static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req // Else if n is 128, noop. // Endloop - // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data, + // The RLE-compressed data is preceded by a 2-byte data count for each row in the data, // which we're going to just skip. stbi__skip(s, h * channelCount * 2 ); @@ -6098,13 +6253,11 @@ 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)) { STBI_FREE(result); result=0; - return nullptr; } *px = x; *py = y; @@ -6138,7 +6291,7 @@ typedef struct int w,h; stbi_uc *out; // output buffer (always 4 components) stbi_uc *background; // The current "background" as far as a gif is concerned - stbi_uc *history; + stbi_uc *history; int flags, bgindex, ratio, transparent, eflags; stbi_uc pal[256][4]; stbi_uc lpal[256][4]; @@ -6212,7 +6365,6 @@ 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 ); @@ -6227,7 +6379,7 @@ static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) { stbi_uc *p, *c; - int idx; + int idx; // recurse to decode the prefixes, since the linked-list is backwards, // and working backwards through an interleaved image would be nasty @@ -6236,12 +6388,12 @@ static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) if (g->cur_y >= g->max_y) return; - idx = g->cur_x + g->cur_y; + idx = g->cur_x + g->cur_y; p = &g->out[idx]; - g->history[idx / 4] = 1; + g->history[idx / 4] = 1; c = &g->color_table[g->codes[code].suffix * 4]; - if (c[3] > 128) { // don't render transparent pixels; + if (c[3] > 128) { // don't render transparent pixels; p[0] = c[2]; p[1] = c[1]; p[2] = c[0]; @@ -6350,31 +6502,36 @@ static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) // two back is the image from two frames ago, used for a very specific disposal format static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) { - int dispose; - int first_frame; - int pi; - int pcount; + int dispose; + int first_frame; + int pi; + int pcount; + STBI_NOTUSED(req_comp); // on first frame, any non-written pixels get the background colour (non-transparent) - first_frame = 0; + first_frame = 0; if (g->out == 0) { - if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header - g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h); - g->background = (stbi_uc *) stbi__malloc(4 * g->w * g->h); - g->history = (stbi_uc *) stbi__malloc(g->w * g->h); - if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory"); - - // image is treated as "tranparent" at the start - ie, nothing overwrites the current background; + if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header + if (!stbi__mad3sizes_valid(4, g->w, g->h, 0)) + return stbi__errpuc("too large", "GIF image is too large"); + pcount = g->w * g->h; + g->out = (stbi_uc *) stbi__malloc(4 * pcount); + g->background = (stbi_uc *) stbi__malloc(4 * pcount); + g->history = (stbi_uc *) stbi__malloc(pcount); + if (!g->out || !g->background || !g->history) + return stbi__errpuc("outofmem", "Out of memory"); + + // image is treated as "transparent" at the start - ie, nothing overwrites the current background; // background colour is only used for pixels that are not rendered first frame, after that "background" - // color refers to teh color that was there the previous frame. - memset( g->out, 0x00, 4 * g->w * g->h ); - memset( g->background, 0x00, 4 * g->w * g->h ); // state of the background (starts transparent) - memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame - first_frame = 1; + // color refers to the color that was there the previous frame. + memset(g->out, 0x00, 4 * pcount); + memset(g->background, 0x00, 4 * pcount); // state of the background (starts transparent) + memset(g->history, 0x00, pcount); // pixels that were affected previous frame + first_frame = 1; } else { // second frame - how do we dispoase of the previous one? - dispose = (g->eflags & 0x1C) >> 2; - pcount = g->w * g->h; + dispose = (g->eflags & 0x1C) >> 2; + pcount = g->w * g->h; if ((dispose == 3) && (two_back == 0)) { dispose = 2; // if I don't have an image to revert back to, default to the old background @@ -6383,32 +6540,32 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i if (dispose == 3) { // use previous graphic for (pi = 0; pi < pcount; ++pi) { if (g->history[pi]) { - memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); + memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); } } - } else if (dispose == 2) { - // restore what was changed last frame to background before that frame; + } else if (dispose == 2) { + // restore what was changed last frame to background before that frame; for (pi = 0; pi < pcount; ++pi) { if (g->history[pi]) { - memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); + memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); } } } else { - // This is a non-disposal case eithe way, so just + // This is a non-disposal case eithe way, so just // leave the pixels as is, and they will become the new background // 1: do not dispose // 0: not specified. } - // background is what out is after the undoing of the previou frame; - memcpy( g->background, g->out, 4 * g->w * g->h ); + // background is what out is after the undoing of the previou frame; + memcpy( g->background, g->out, 4 * g->w * g->h ); } - // clear my history; + // clear my history; memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame for (;;) { - int tag = stbi__get8(s); + int tag = stbi__get8(s); switch (tag) { case 0x2C: /* Image Descriptor */ { @@ -6430,6 +6587,13 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i g->cur_x = g->start_x; g->cur_y = g->start_y; + // if the width of the specified rectangle is 0, that means + // we may not see *any* pixels or the image is malformed; + // to make sure this is caught, move the current y down to + // max_y (which is what out_gif_code checks). + if (w == 0) + g->cur_y = g->max_y; + g->lflags = stbi__get8(s); if (g->lflags & 0x40) { @@ -6446,19 +6610,19 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i } else if (g->flags & 0x80) { g->color_table = (stbi_uc *) g->pal; } else - return stbi__errpuc("missing color table", "Corrupt GIF"); - + return stbi__errpuc("missing color table", "Corrupt GIF"); + o = stbi__process_gif_raster(s, g); - if (o == NULL) return NULL; + if (!o) return NULL; - // if this was the first frame, - pcount = g->w * g->h; + // if this was the first frame, + pcount = g->w * g->h; if (first_frame && (g->bgindex > 0)) { // if first frame, any pixel not drawn to gets the background color for (pi = 0; pi < pcount; ++pi) { if (g->history[pi] == 0) { - g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; - memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); + g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; + memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); } } } @@ -6469,7 +6633,7 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i case 0x21: // Comment Extension. { int len; - int ext = stbi__get8(s); + int ext = stbi__get8(s); if (ext == 0xF9) { // Graphic Control Extension. len = stbi__get8(s); if (len == 4) { @@ -6478,23 +6642,23 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i // unset old transparent if (g->transparent >= 0) { - g->pal[g->transparent][3] = 255; - } + g->pal[g->transparent][3] = 255; + } if (g->eflags & 0x01) { g->transparent = stbi__get8(s); if (g->transparent >= 0) { - g->pal[g->transparent][3] = 0; + g->pal[g->transparent][3] = 0; } } else { // don't need transparent - stbi__skip(s, 1); - g->transparent = -1; + stbi__skip(s, 1); + g->transparent = -1; } } else { stbi__skip(s, len); break; } - } + } while ((len = stbi__get8(s)) != 0) { stbi__skip(s, len); } @@ -6513,15 +6677,15 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i 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)) { - int layers = 0; + int layers = 0; stbi_uc *u = 0; stbi_uc *out = 0; - stbi_uc *two_back = 0; + stbi_uc *two_back = 0; stbi__gif g; - int stride; + int stride; memset(&g, 0, sizeof(g)); if (delays) { - *delays = 0; + *delays = 0; } do { @@ -6531,46 +6695,52 @@ static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, if (u) { *x = g.w; *y = g.h; - ++layers; - stride = g.w * g.h * 4; - + ++layers; + stride = g.w * g.h * 4; + if (out) { - out = (stbi_uc*) STBI_REALLOC( out, layers * stride ); + void *tmp = (stbi_uc*) STBI_REALLOC( out, layers * stride ); + if (NULL == tmp) { + STBI_FREE(g.out); + STBI_FREE(g.history); + STBI_FREE(g.background); + return stbi__errpuc("outofmem", "Out of memory"); + } + else + out = (stbi_uc*) tmp; if (delays) { - *delays = (int*) STBI_REALLOC( *delays, sizeof(int) * layers ); + *delays = (int*) STBI_REALLOC( *delays, sizeof(int) * layers ); } } else { - out = (stbi_uc*)stbi__malloc( layers * stride ); - if (!out) return stbi__errpuc("outofmem", "Out of memory"); + out = (stbi_uc*)stbi__malloc( layers * stride ); if (delays) { - *delays = (int*) stbi__malloc( layers * sizeof(int) ); - if (!*delays) return stbi__errpuc("outofmem", "Out of memory"); + *delays = (int*) stbi__malloc( layers * sizeof(int) ); } } - memcpy( out + ((layers - 1) * stride), u, stride ); + memcpy( out + ((layers - 1) * stride), u, stride ); if (layers >= 2) { - two_back = out - 2 * stride; + two_back = out - 2 * stride; } - if (delays && *delays) { - (*delays)[layers - 1U] = g.delay; + if (delays) { + (*delays)[layers - 1U] = g.delay; } } - } while (u != 0); + } while (u != 0); - // free temp buffer; - STBI_FREE(g.out); - STBI_FREE(g.history); - STBI_FREE(g.background); + // free temp buffer; + STBI_FREE(g.out); + STBI_FREE(g.history); + STBI_FREE(g.background); - // do the final conversion after loading everything; + // do the final conversion after loading everything; if (req_comp && req_comp != 4) out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); - *z = layers; + *z = layers; return out; } else { - return stbi__errpuc("not GIF", "Image was not as a gif type."); + return stbi__errpuc("not GIF", "Image was not as a gif type."); } } @@ -6579,6 +6749,7 @@ static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req stbi_uc *u = 0; stbi__gif g; memset(&g, 0, sizeof(g)); + STBI_NOTUSED(ri); u = stbi__gif_load_next(s, &g, comp, req_comp, 0); if (u == (stbi_uc *) s) u = 0; // end of animated gif marker @@ -6587,14 +6758,17 @@ static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req *y = g.h; // moved conversion to after successful load so that the same - // can be done for multiple frames. + // can be done for multiple frames. if (req_comp && req_comp != 4) u = stbi__convert_format(u, 4, req_comp, g.w, g.h); + } else if (g.out) { + // if there was an error and we allocated an image buffer, free it! + STBI_FREE(g.out); } - // free buffers needed for multiple frame loading; + // free buffers needed for multiple frame loading; STBI_FREE(g.history); - STBI_FREE(g.background); + STBI_FREE(g.background); return u; } @@ -6866,7 +7040,12 @@ static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) return 0; if (x) *x = s->img_x; if (y) *y = s->img_y; - if (comp) *comp = info.ma ? 4 : 3; + if (comp) { + if (info.bpp == 24 && info.ma == 0xff000000) + *comp = 3; + else + *comp = info.ma ? 4 : 3; + } return 1; } #endif @@ -7252,6 +7431,7 @@ STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user /* revision history: + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs 2.19 (2018-02-11) fix warning 2.18 (2018-01-30) fix warnings 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug @@ -7474,5 +7654,3 @@ 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. ------------------------------------------------------------------------------ */ - -#pragma clang diagnostic pop diff --git a/apps/common/external/stb_image/stb_image_write.h b/apps/common/external/stb_image/stb_image_write.h index c05e95812b..cffd473c1f 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.09 - public domain - http://nothings.org/stb/stb_image_write.h +/* stb_image_write - v1.14 - 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 @@ -10,15 +10,9 @@ Will probably not work correctly with strict-aliasing optimizations. - If using a modern Microsoft Compiler, non-safe versions of CRT calls may cause - compilation warnings or even errors. To avoid this, also before #including, - - #define STBI_MSC_SECURE_CRT - ABOUT: - This header file is a library for writing images to C stdio. It could be - adapted to write to memory or a general streaming interface; let me know. + This header file is a library for writing images to C stdio or a callback. The PNG output is not optimal; it is 20-50% larger than the file written by a decent optimizing implementation; though providing a custom @@ -38,6 +32,14 @@ The returned data will be freed with STBIW_FREE() (free() by default), so it must be heap allocated with STBIW_MALLOC() (malloc() by default), +UNICODE: + + If compiling for Windows and you wish to use Unicode filenames, compile + with + #define STBIW_WINDOWS_UTF8 + and pass utf8-encoded filenames. Call stbiw_convert_wchar_to_utf8 to convert + Windows wchar_t filenames to utf8. + USAGE: There are five functions, one for each image file format: @@ -103,7 +105,7 @@ TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed data, set the global variable 'stbi_write_tga_with_rle' to 0. - + JPEG does ignore alpha channels in input data; quality is between 1 and 100. Higher quality looks better but results in a bigger image. JPEG baseline (no JPEG progressive). @@ -111,7 +113,7 @@ CREDITS: - Sean Barrett - PNG/BMP/TGA + Sean Barrett - PNG/BMP/TGA Baldur Karlsson - HDR Jean-Sebastien Guay - TGA monochrome Tim Kelsey - misc enhancements @@ -148,6 +150,8 @@ LICENSE #ifndef INCLUDE_STB_IMAGE_WRITE_H #define INCLUDE_STB_IMAGE_WRITE_H +#include + // if STB_IMAGE_WRITE_STATIC causes problems, try defining STBIWDEF to 'inline' or 'static inline' #ifndef STBIWDEF #ifdef STB_IMAGE_WRITE_STATIC @@ -173,6 +177,10 @@ STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); 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 +STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); +#endif #endif typedef void stbi_write_func(void *context, void *data, int size); @@ -239,17 +247,17 @@ STBIWDEF void stbi_flip_vertically_on_write(int flip_boolean); #define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff) #ifdef STB_IMAGE_WRITE_STATIC -static int stbi__flip_vertically_on_write=0; static int stbi_write_png_compression_level = 8; static int stbi_write_tga_with_rle = 1; static int stbi_write_force_png_filter = -1; #else int stbi_write_png_compression_level = 8; -int stbi__flip_vertically_on_write=0; int stbi_write_tga_with_rle = 1; int stbi_write_force_png_filter = -1; #endif +static int stbi__flip_vertically_on_write = 0; + STBIWDEF void stbi_flip_vertically_on_write(int flag) { stbi__flip_vertically_on_write = flag; @@ -275,15 +283,52 @@ static void stbi__stdio_write(void *context, void *data, int size) fwrite(data,1,size,(FILE*) context); } -static int stbi__start_write_file(stbi__write_context *s, const char *filename) +#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) +#ifdef __cplusplus +#define STBIW_EXTERN extern "C" +#else +#define STBIW_EXTERN extern +#endif +STBIW_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); +STBIW_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); + +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); +} +#endif + +static FILE *stbiw__fopen(char const *filename, char const *mode) { FILE *f; -#ifdef STBI_MSC_SECURE_CRT - if (fopen_s(&f, filename, "wb")) - f = NULL; +#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) + wchar_t wMode[64]; + wchar_t wFilename[1024]; + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename))) + return 0; + + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode))) + return 0; + +#if _MSC_VER >= 1400 + if (0 != _wfopen_s(&f, wFilename, wMode)) + f = 0; +#else + f = _wfopen(wFilename, wMode); +#endif + +#elif defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != fopen_s(&f, filename, mode)) + f=0; #else - f = fopen(filename, "wb"); + f = fopen(filename, mode); #endif + return f; +} + +static int stbi__start_write_file(stbi__write_context *s, const char *filename) +{ + FILE *f = stbiw__fopen(filename, "wb"); stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f); return f != NULL; } @@ -343,7 +388,7 @@ static void stbiw__putc(stbi__write_context *s, unsigned char c) static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c) { unsigned char arr[3]; - arr[0] = a, arr[1] = b, arr[2] = c; + arr[0] = a; arr[1] = b; arr[2] = c; s->func(s->context, arr, 3); } @@ -391,10 +436,11 @@ static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, i if (stbi__flip_vertically_on_write) vdir *= -1; - if (vdir < 0) - j_end = -1, j = y-1; - else - j_end = y, j = 0; + if (vdir < 0) { + j_end = -1; j = y-1; + } else { + j_end = y; j = 0; + } for (; j != j_end; j += vdir) { for (i=0; i < x; ++i) { @@ -552,7 +598,7 @@ STBIWDEF int stbi_write_tga(char const *filename, int x, int y, int comp, const #define stbiw__max(a, b) ((a) > (b) ? (a) : (b)) -void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) +static void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) { int exponent; float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2])); @@ -569,7 +615,7 @@ void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) } } -void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte) +static void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte) { unsigned char lengthbyte = STBIW_UCHAR(length+128); STBIW_ASSERT(length+128 <= 255); @@ -577,7 +623,7 @@ void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char dat s->func(s->context, &databyte, 1); } -void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data) +static void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data) { unsigned char lengthbyte = STBIW_UCHAR(length); STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code @@ -585,7 +631,7 @@ void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *d s->func(s->context, data, length); } -void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline) +static void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline) { unsigned char scanlineheader[4] = { 2, 2, 0, 0 }; unsigned char rgbe[4]; @@ -686,15 +732,15 @@ 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 STBI_MSC_SECURE_CRT - len = sprintf_s(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); +#ifdef __STDC_WANT_SECURE_LIB__ + 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); #endif s->func(s->context, buffer, len); for(i=0; i < y; i++) - stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*x*(stbi__flip_vertically_on_write ? y-1-i : i)*x); + stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*x*(stbi__flip_vertically_on_write ? y-1-i : i)); STBIW_FREE(scratch); return 1; } @@ -728,7 +774,7 @@ STBIWDEF int stbi_write_hdr(char const *filename, int x, int y, int comp, const #ifndef STBIW_ZLIB_COMPRESS // stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size() -#define stbiw__sbraw(a) ((int *) (a) - 2) +#define stbiw__sbraw(a) ((int *) (void *) (a) - 2) #define stbiw__sbm(a) stbiw__sbraw(a)[0] #define stbiw__sbn(a) stbiw__sbraw(a)[1] @@ -809,7 +855,7 @@ static unsigned int stbiw__zhash(unsigned char *data) #endif // STBIW_ZLIB_COMPRESS -unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality) +STBIWDEF unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality) { #ifdef STBIW_ZLIB_COMPRESS // user provided a zlib compress implementation, use that @@ -822,7 +868,7 @@ unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_l unsigned int bitbuf=0; int i,j, bitcount=0; unsigned char *out = NULL; - unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(char**)); + unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(unsigned char**)); if (hash_table == NULL) return NULL; if (quality < 5) quality = 5; @@ -845,7 +891,7 @@ unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_l for (j=0; j < n; ++j) { if (hlist[j]-data > i-32768) { // if entry lies within window int d = stbiw__zlib_countm(hlist[j], data+i, data_len-i); - if (d >= best) best=d,bestloc=hlist[j]; + if (d >= best) { best=d; bestloc=hlist[j]; } } } // when hash table entry is too long, delete half the entries @@ -904,8 +950,8 @@ unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_l int blocklen = (int) (data_len % 5552); j=0; while (j < data_len) { - for (i=0; i < blocklen; ++i) s1 += data[j+i], s2 += s1; - s1 %= 65521, s2 %= 65521; + for (i=0; i < blocklen; ++i) { s1 += data[j+i]; s2 += s1; } + s1 %= 65521; s2 %= 65521; j += blocklen; blocklen = 5552; } @@ -923,6 +969,9 @@ unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_l static unsigned int stbiw__crc32(unsigned char *buffer, int len) { +#ifdef STBIW_CRC32 + return STBIW_CRC32(buffer, len); +#else static unsigned int crc_table[256] = { 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, @@ -964,6 +1013,7 @@ static unsigned int stbiw__crc32(unsigned char *buffer, int len) for (i=0; i < len; ++i) crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)]; return ~crc; +#endif } #define stbiw__wpng4(o,a,b,c,d) ((o)[0]=STBIW_UCHAR(a),(o)[1]=STBIW_UCHAR(b),(o)[2]=STBIW_UCHAR(c),(o)[3]=STBIW_UCHAR(d),(o)+=4) @@ -994,9 +1044,15 @@ static void stbiw__encode_png_line(unsigned char *pixels, int stride_bytes, int int type = mymap[filter_type]; unsigned char *z = pixels + stride_bytes * (stbi__flip_vertically_on_write ? height-1-y : y); int signed_stride = stbi__flip_vertically_on_write ? -stride_bytes : stride_bytes; + + if (type==0) { + memcpy(line_buffer, z, width*n); + return; + } + + // first loop isn't optimized since it's just one pixel for (i = 0; i < n; ++i) { switch (type) { - case 0: line_buffer[i] = z[i]; break; case 1: line_buffer[i] = z[i]; break; case 2: line_buffer[i] = z[i] - z[i-signed_stride]; break; case 3: line_buffer[i] = z[i] - (z[i-signed_stride]>>1); break; @@ -1005,20 +1061,17 @@ static void stbiw__encode_png_line(unsigned char *pixels, int stride_bytes, int case 6: line_buffer[i] = z[i]; break; } } - for (i=n; i < width*n; ++i) { - switch (type) { - case 0: line_buffer[i] = z[i]; break; - case 1: line_buffer[i] = z[i] - z[i-n]; break; - case 2: line_buffer[i] = z[i] - z[i-signed_stride]; break; - case 3: line_buffer[i] = z[i] - ((z[i-n] + z[i-signed_stride])>>1); break; - case 4: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-signed_stride], z[i-signed_stride-n]); break; - case 5: line_buffer[i] = z[i] - (z[i-n]>>1); break; - case 6: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break; - } + switch (type) { + case 1: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-n]; break; + case 2: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-signed_stride]; break; + case 3: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - ((z[i-n] + z[i-signed_stride])>>1); break; + case 4: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-signed_stride], z[i-signed_stride-n]); break; + case 5: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - (z[i-n]>>1); break; + case 6: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break; } } -unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len) +STBIWDEF unsigned char *stbi_write_png_to_mem(const unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len) { int force_filter = stbi_write_force_png_filter; int ctype[5] = { -1, 0, 4, 2, 6 }; @@ -1040,11 +1093,11 @@ unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, in int filter_type; if (force_filter > -1) { filter_type = force_filter; - stbiw__encode_png_line(pixels, stride_bytes, x, y, j, n, force_filter, line_buffer); + stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, force_filter, line_buffer); } else { // Estimate the best filter by running through all of them: int best_filter = 0, best_filter_val = 0x7fffffff, est, i; for (filter_type = 0; filter_type < 5; filter_type++) { - stbiw__encode_png_line(pixels, stride_bytes, x, y, j, n, filter_type, line_buffer); + stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, filter_type, line_buffer); // Estimate the entropy of the line using this filter; the less, the better. est = 0; @@ -1057,7 +1110,7 @@ unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, in } } if (filter_type != best_filter) { // If the last iteration already got us the best filter, don't redo it - stbiw__encode_png_line(pixels, stride_bytes, x, y, j, n, best_filter, line_buffer); + stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, best_filter, line_buffer); filter_type = best_filter; } } @@ -1109,14 +1162,10 @@ STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const { FILE *f; int len; - unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len); + unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len); if (png == NULL) return 0; -#ifdef STBI_MSC_SECURE_CRT - if (fopen_s(&f, filename, "wb")) - f = NULL; -#else - f = fopen(filename, "wb"); -#endif + + f = stbiw__fopen(filename, "wb"); if (!f) { STBIW_FREE(png); return 0; } fwrite(png, 1, len, f); fclose(f); @@ -1128,7 +1177,7 @@ STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int stride_bytes) { int len; - unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len); + unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len); if (png == NULL) return 0; func(context, png, len); STBIW_FREE(png); @@ -1222,26 +1271,31 @@ static void stbiw__jpg_calcBits(int val, unsigned short bits[2]) { bits[0] = val & ((1< 100 ? 100 : quality; quality = quality < 50 ? 5000 / quality : 200 - quality * 2; @@ -1390,7 +1445,7 @@ static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, in static const unsigned char head0[] = { 0xFF,0xD8,0xFF,0xE0,0,0x10,'J','F','I','F',0,1,1,0,0,1,0,1,0,0,0xFF,0xDB,0,0x84,0 }; static const unsigned char head2[] = { 0xFF,0xDA,0,0xC,3,1,0,2,0x11,3,0x11,0,0x3F,0 }; const unsigned char head1[] = { 0xFF,0xC0,0,0x11,8,(unsigned char)(height>>8),STBIW_UCHAR(height),(unsigned char)(width>>8),STBIW_UCHAR(width), - 3,1,0x11,0,2,0x11,1,3,0x11,1,0xFF,0xC4,0x01,0xA2,0 }; + 3,1,(unsigned char)(subsample?0x22:0x11),0,2,0x11,1,3,0x11,1,0xFF,0xC4,0x01,0xA2,0 }; s->func(s->context, (void*)head0, sizeof(head0)); s->func(s->context, (void*)YTable, sizeof(YTable)); stbiw__putc(s, 1); @@ -1413,38 +1468,74 @@ static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, in // Encode 8x8 macroblocks { static const unsigned short fillBits[] = {0x7F, 7}; - const unsigned char *imageData = (const unsigned char *)data; int DCY=0, DCU=0, DCV=0; int bitBuf=0, bitCnt=0; // comp == 2 is grey+alpha (alpha is ignored) int ofsG = comp > 2 ? 1 : 0, ofsB = comp > 2 ? 2 : 0; + const unsigned char *dataR = (const unsigned char *)data; + const unsigned char *dataG = dataR + ofsG; + const unsigned char *dataB = dataR + ofsB; int x, y, pos; - for(y = 0; y < height; y += 8) { - for(x = 0; x < width; x += 8) { - float YDU[64], UDU[64], VDU[64]; - for(row = y, pos = 0; row < y+8; ++row) { - for(col = x; col < x+8; ++col, ++pos) { - int p = (stbi__flip_vertically_on_write ? height-1-row : row)*width*comp + col*comp; - float r, g, b; - if(row >= height) { - p -= width*comp*(row+1 - height); + if(subsample) { + for(y = 0; y < height; y += 16) { + for(x = 0; x < width; x += 16) { + float Y[256], U[256], V[256]; + for(row = y, pos = 0; row < y+16; ++row) { + // row >= height => use last input row + int clamped_row = (row < height) ? row : height - 1; + int base_p = (stbi__flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp; + for(col = x; col < x+16; ++col, ++pos) { + // if col >= width => use pixel from last input column + int p = base_p + ((col < width) ? col : (width-1))*comp; + float r = dataR[p], g = dataG[p], b = dataB[p]; + Y[pos]= +0.29900f*r + 0.58700f*g + 0.11400f*b - 128; + U[pos]= -0.16874f*r - 0.33126f*g + 0.50000f*b; + V[pos]= +0.50000f*r - 0.41869f*g - 0.08131f*b; } - if(col >= width) { - p -= comp*(col+1 - width); + } + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+0, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+8, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+128, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+136, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); + + // subsample U,V + { + float subU[64], subV[64]; + int yy, xx; + for(yy = 0, pos = 0; yy < 8; ++yy) { + for(xx = 0; xx < 8; ++xx, ++pos) { + int j = yy*32+xx*2; + subU[pos] = (U[j+0] + U[j+1] + U[j+16] + U[j+17]) * 0.25f; + subV[pos] = (V[j+0] + V[j+1] + V[j+16] + V[j+17]) * 0.25f; + } } - - r = imageData[p+0]; - g = imageData[p+ofsG]; - b = imageData[p+ofsB]; - YDU[pos]=+0.29900f*r+0.58700f*g+0.11400f*b-128; - UDU[pos]=-0.16874f*r-0.33126f*g+0.50000f*b; - VDU[pos]=+0.50000f*r-0.41869f*g-0.08131f*b; + DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subU, 8, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); + DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subV, 8, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); } } + } + } else { + for(y = 0; y < height; y += 8) { + for(x = 0; x < width; x += 8) { + float Y[64], U[64], V[64]; + for(row = y, pos = 0; row < y+8; ++row) { + // row >= height => use last input row + int clamped_row = (row < height) ? row : height - 1; + int base_p = (stbi__flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp; + for(col = x; col < x+8; ++col, ++pos) { + // if col >= width => use pixel from last input column + int p = base_p + ((col < width) ? col : (width-1))*comp; + float r = dataR[p], g = dataG[p], b = dataB[p]; + Y[pos]= +0.29900f*r + 0.58700f*g + 0.11400f*b - 128; + U[pos]= -0.16874f*r - 0.33126f*g + 0.50000f*b; + V[pos]= +0.50000f*r - 0.41869f*g - 0.08131f*b; + } + } - DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT); - DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, UDU, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); - DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, VDU, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y, 8, fdtbl_Y, DCY, YDC_HT, YAC_HT); + DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, U, 8, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); + DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, V, 8, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); + } } } @@ -1483,6 +1574,13 @@ STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const #endif // STB_IMAGE_WRITE_IMPLEMENTATION /* Revision history + 1.14 (2020-02-02) updated JPEG writer to downsample chroma channels + 1.13 + 1.12 + 1.11 (2019-08-11) + + 1.10 (2019-02-07) + support utf8 filenames in Windows; fix warnings and platform ifdefs 1.09 (2018-02-11) fix typo in zlib quality API, improve STB_I_W_STATIC in C++ 1.08 (2018-01-29) @@ -1531,38 +1629,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/ospray_testing/CMakeLists.txt b/apps/common/ospray_testing/CMakeLists.txt index ed8ab59f65..29ae8ca955 100644 --- a/apps/common/ospray_testing/CMakeLists.txt +++ b/apps/common/ospray_testing/CMakeLists.txt @@ -1,9 +1,11 @@ -## Copyright 2009-2019 Intel Corporation +## Copyright 2009-2020 Intel Corporation ## SPDX-License-Identifier: Apache-2.0 include(GenerateExportHeader) add_library(ospray_testing SHARED + ${OSPRAY_RESOURCE} + ospray_testing.cpp builders/Boxes.cpp @@ -17,6 +19,8 @@ add_library(ospray_testing SHARED builders/Streamlines.cpp builders/SubdivisionCube.cpp builders/UnstructuredVolume.cpp + builders/Planes.cpp + builders/ClippingGeometries.cpp # regression test scenes builders/test_pt_glass.cpp diff --git a/apps/common/ospray_testing/builders/Builder.cpp b/apps/common/ospray_testing/builders/Builder.cpp index 2ff429b9fd..aeae654e84 100644 --- a/apps/common/ospray_testing/builders/Builder.cpp +++ b/apps/common/ospray_testing/builders/Builder.cpp @@ -1,4 +1,4 @@ -// Copyright 2009-2019 Intel Corporation +// Copyright 2009-2020 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "Builder.h" @@ -16,16 +16,22 @@ void Builder::commit() } cpp::World Builder::buildWorld() const +{ + return buildWorld({}); +} + +cpp::World Builder::buildWorld( + const std::vector &instances) const { cpp::World world; auto group = buildGroup(); - cpp::Instance inst(group); - inst.commit(); + cpp::Instance instance(group); + instance.commit(); - std::vector instances; - instances.push_back(inst); + std::vector inst = instances; + inst.push_back(instance); if (addPlane) { auto bounds = group.getBounds(); @@ -40,10 +46,10 @@ cpp::World Builder::buildWorld() const cpp::Instance planeInst(planeGroup); planeInst.commit(); - instances.push_back(planeInst); + inst.push_back(planeInst); } - world.setParam("instance", cpp::Data(instances)); + world.setParam("instance", cpp::Data(inst)); cpp::Light light("ambient"); light.setParam("visible", false); diff --git a/apps/common/ospray_testing/builders/Builder.h b/apps/common/ospray_testing/builders/Builder.h index 3908287f78..513c82d69d 100644 --- a/apps/common/ospray_testing/builders/Builder.h +++ b/apps/common/ospray_testing/builders/Builder.h @@ -1,4 +1,4 @@ -// Copyright 2009-2019 Intel Corporation +// Copyright 2009-2020 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once @@ -27,6 +27,8 @@ struct Builder : public memory::RefCountedObject, virtual cpp::Group buildGroup() const = 0; virtual cpp::World buildWorld() const; + virtual cpp::World buildWorld( + const std::vector &instances) const; protected: cpp::TransferFunction makeTransferFunction(const vec2f &valueRange) const; diff --git a/apps/common/ospray_testing/builders/ClippingGeometries.cpp b/apps/common/ospray_testing/builders/ClippingGeometries.cpp new file mode 100644 index 0000000000..aa2b536fc3 --- /dev/null +++ b/apps/common/ospray_testing/builders/ClippingGeometries.cpp @@ -0,0 +1,265 @@ +// Copyright 2020 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#include "Builder.h" +#include "ospray_testing.h" +// ospcommon +#include "ospcommon/utility/multidim_index_sequence.h" + +using namespace ospcommon::math; + +namespace ospray { +namespace testing { + +struct ClippingGeometries : public detail::Builder +{ + ClippingGeometries( + const std::string &geometryType, const std::string &geometrySubType = ""); + ~ClippingGeometries() override = default; + + void commit() override; + + cpp::Group buildGroup() const override; + cpp::World buildWorld() const override; + + private: + vec3i dimensions{19}; + std::string geomType; + std::string geomSubType; +}; + +// Inlined definitions //////////////////////////////////////////////////// + +ClippingGeometries::ClippingGeometries( + const std::string &geometryType, const std::string &geometrySubType) + : geomType(geometryType), geomSubType(geometrySubType) +{} + +void ClippingGeometries::commit() +{ + Builder::commit(); + dimensions = getParam("dimensions", dimensions); + geomType = getParam("geometryType", geomType); + geomSubType = getParam("geometrySubType", geomSubType); + addPlane = false; +} + +cpp::Group ClippingGeometries::buildGroup() const +{ + cpp::Geometry spheresGeometry("sphere"); + + index_sequence_3D numSpheres(dimensions); + std::vector positions; + + auto dim = reduce_max(dimensions) - 1; + float size = 2.f; + for (auto i : numSpheres) { + vec3f i_f = static_cast(i); + vec3f p = size * (i_f / dim - .5f); + positions.emplace_back(p); + } + + spheresGeometry.setParam("sphere.position", cpp::Data(positions)); + spheresGeometry.setParam("radius", size / dim / 2.f); + spheresGeometry.commit(); + + cpp::GeometricModel model(spheresGeometry); + if (rendererType == "pathtracer" || rendererType == "scivis") { + cpp::Material material(rendererType, "obj"); + material.setParam("kd", vec3f(.1f, .4f, .8f)); + material.commit(); + model.setParam("material", material); + } + model.commit(); + + cpp::Group group; + group.setParam("geometry", cpp::Data(model)); + group.commit(); + + return group; +} + +cpp::World ClippingGeometries::buildWorld() const +{ + // Create selected geometry + vec3f cPos; + cpp::Geometry geometry(geomType); + if (geomType == "curve") { + static std::vector vertices = {{-1.f, +0.f, -1.f, 0.2f}, + {+0.f, -1.f, +0.f, 0.2f}, + {+1.f, +0.f, +1.f, 0.2f}, + {-1.f, +0.f, +1.f, 0.2f}, + {+0.f, +1.f, +0.f, 0.3f}, + {+1.f, +0.f, -1.f, 0.2f}, + {-1.f, +0.f, -1.f, 0.2f}, + {+0.f, -1.f, +0.f, 0.2f}, + {+1.f, +0.f, +1.f, 0.2f}}; + if (geomSubType == "bspline") { + geometry.setParam("vertex.position_radius", cpp::Data(vertices)); + geometry.setParam("basis", int(OSP_BSPLINE)); + cPos = vec3f(-.1f, 0.f, -.1f); + } else if (geomSubType == "linear") { + geometry.setParam("vertex.position", + cpp::Data(vertices.size(), sizeof(vec4f), (vec3f *)vertices.data())); + geometry.setParam("radius", 0.2f); + geometry.setParam("basis", int(OSP_LINEAR)); + cPos = vec3f(-.2f, 0.f, -.2f); + } + std::vector indices = {0, 1, 2, 3, 4, 5}; + geometry.setParam("index", cpp::Data(indices)); + geometry.setParam("type", int(OSP_ROUND)); + geometry.commit(); + } else if (geomType == "subdivision") { + const float size = .9f; + std::vector vertices = {{-size, -size, -size}, + {+size, -size, -size}, + {+size, -size, +size}, + {-size, -size, +size}, + {-size, +size, -size}, + {+size, +size, -size}, + {+size, +size, +size}, + {-size, +size, +size}}; + geometry.setParam("vertex.position", cpp::Data(vertices)); + std::vector faces = {4, 4, 4, 4, 4, 4}; + geometry.setParam("face", cpp::Data(faces)); + std::vector indices = { + 0, + 4, + 5, + 1, + 1, + 5, + 6, + 2, + 2, + 6, + 7, + 3, + 0, + 3, + 7, + 4, + 4, + 7, + 6, + 5, + 0, + 1, + 2, + 3, + }; + geometry.setParam("index", cpp::Data(indices)); + std::vector vertexCreaseIndices = {0, 1, 2, 3, 4, 5, 6, 7}; + geometry.setParam("vertexCrease.index", cpp::Data(vertexCreaseIndices)); + std::vector vertexCreaseWeights = { + 2.f, 2.f, 2.f, 2.f, 2.f, 2.f, 2.f, 2.f}; + geometry.setParam("vertexCrease.weight", cpp::Data(vertexCreaseWeights)); + std::vector edgeCreaseIndices = {{0, 1}, + {1, 2}, + {2, 3}, + {3, 0}, + {4, 5}, + {5, 6}, + {6, 7}, + {7, 4}, + {0, 4}, + {1, 5}, + {2, 6}, + {3, 7}}; + geometry.setParam("edgeCrease.index", cpp::Data(edgeCreaseIndices)); + std::vector edgeCreaseWeights = { + 2.f, 2.f, 2.f, 2.f, 2.f, 2.f, 2.f, 2.f, 2.f, 2.f, 2.f, 2.f}; + geometry.setParam("edgeCrease.weight", cpp::Data(edgeCreaseWeights)); + geometry.setParam("level", 10.f); + geometry.commit(); + cPos = vec3f(-.3f, .3f, -.3f); + } else if (geomType == "mesh") { + const vec3f pos = vec3f(0.f); + const vec3f size = vec3f(1.1f); + std::vector vertices = {{pos.x, pos.y - size.y, pos.z}, + {pos.x - size.x, pos.y, pos.z - size.z}, + {pos.x + size.x, pos.y, pos.z - size.z}, + {pos.x + size.x, pos.y, pos.z + size.z}, + {pos.x - size.x, pos.y, pos.z + size.z}, + {pos.x, pos.y + size.y, pos.z}}; + geometry.setParam("vertex.position", cpp::Data(vertices)); + std::vector indices = {{0, 1, 2}, + {0, 2, 3}, + {0, 3, 4}, + {0, 4, 1}, + {5, 2, 1}, + {5, 3, 2}, + {5, 4, 3}, + {5, 1, 4}}; + geometry.setParam("index", cpp::Data(indices)); + geometry.commit(); + cPos = vec3f(-.3f, 0.f, -.3f); + } else if (geomType == "plane") { + std::vector coefficients = {vec4f(1.0f, 1.0f, 1.0f, 0.0f)}; + geometry.setParam("plane.coefficients", cpp::Data(coefficients)); + geometry.commit(); + cPos = vec3f(-.3f, .2f, -.3f); + } else if (geomType == "box") { + std::vector boxes = { + box3f(vec3f(-.9f, -.9f, -.9f), vec3f(.9f, .9f, .9f))}; + geometry.setParam("box", cpp::Data(boxes)); + geometry.commit(); + cPos = vec3f(-.3f, .3f, -.3f); + } else if (geomType == "sphere") { + std::vector position = {vec3f(0.f, 0.f, 0.f)}; + geometry.setParam("sphere.position", cpp::Data(position)); + geometry.setParam("radius", 1.f); + geometry.commit(); + cPos = vec3f(-.3f, .2f, -.3f); + } else + return Builder::buildWorld(); + + std::vector inst; + + // Create clipping instance with model that has inverted normals + { + cpp::GeometricModel model(geometry); + model.setParam("invertNormals", true); + model.commit(); + + cpp::Group group; + group.setParam("clippingGeometry", cpp::Data(model)); + group.commit(); + + cpp::Instance instance(group); + instance.commit(); + inst.push_back(instance); + } + + // Create second clipping instance but keep original normals + { + cpp::GeometricModel model(geometry); + model.setParam("invertNormals", false); + model.commit(); + + cpp::Group group; + group.setParam("clippingGeometry", cpp::Data(model)); + group.commit(); + + cpp::Instance instance(group); + instance.setParam("xfm", affine3f::translate(cPos)); + instance.commit(); + + inst.push_back(instance); + } + + return Builder::buildWorld(inst); +} + +OSP_REGISTER_TESTING_BUILDER(ClippingGeometries("sphere"), clip_with_spheres); +OSP_REGISTER_TESTING_BUILDER(ClippingGeometries("box"), clip_with_boxes); +OSP_REGISTER_TESTING_BUILDER(ClippingGeometries("plane"), clip_with_planes); +OSP_REGISTER_TESTING_BUILDER(ClippingGeometries("mesh"), clip_with_meshes); +OSP_REGISTER_TESTING_BUILDER( + ClippingGeometries("subdivision"), clip_with_subdivisions); +OSP_REGISTER_TESTING_BUILDER( + ClippingGeometries("curve", "linear"), clip_with_linear_curves); +OSP_REGISTER_TESTING_BUILDER( + ClippingGeometries("curve", "bspline"), clip_with_bspline_curves); +} // namespace testing +} // namespace ospray diff --git a/apps/common/ospray_testing/builders/CornellBox.cpp b/apps/common/ospray_testing/builders/CornellBox.cpp index 4595ba45ea..c8cabf4366 100644 --- a/apps/common/ospray_testing/builders/CornellBox.cpp +++ b/apps/common/ospray_testing/builders/CornellBox.cpp @@ -1,4 +1,4 @@ -// Copyright 2009-2019 Intel Corporation +// Copyright 2009-2020 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "Builder.h" @@ -273,7 +273,51 @@ cpp::World CornellBox::buildWorld() const return world; } +struct CornellBoxPhotometric : public CornellBox +{ + CornellBoxPhotometric() = default; + ~CornellBoxPhotometric() override = default; + + cpp::World buildWorld() const override; +}; + +cpp::World CornellBoxPhotometric::buildWorld() const +{ + auto world = CornellBox::buildWorld(); + + cpp::Light light1d("spot"); + light1d.setParam("intensity", 5.f); + light1d.setParam("position", vec3f(-0.6f, 0.8f, -0.5f)); + light1d.setParam("direction", vec3f(0.0f, -1.0f, 0.0f)); + light1d.setParam("openingAngle", 360.f); + light1d.setParam("penumbraAngle", 0.f); + float lid1d[] = {2.5f, 0.4f, 0.2f, 0.1f, 0.03f, 0.01f, 0.01f}; + light1d.setParam("intensityDistribution", cpp::Data(7, lid1d)); + light1d.commit(); + + cpp::Light light2d("spot"); + light2d.setParam("intensity", 1.f); + light2d.setParam("position", vec3f(0.3f, 0.6f, 0.f)); + light2d.setParam("direction", vec3f(0.0f, -1.0f, 0.0f)); + light2d.setParam("openingAngle", 270.f); + light2d.setParam("penumbraAngle", 10.f); + 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::Data(vec2ul(5, 12), lid2d)); + light2d.setParam("c0", vec3f(1.0f, 0.0f, 0.0f)); + light2d.commit(); + + std::vector lights; + lights.clear(); + lights.push_back(light1d); + lights.push_back(light2d); + world.setParam("light", cpp::Data(lights)); + + return world; +} + OSP_REGISTER_TESTING_BUILDER(CornellBox, cornell_box); +OSP_REGISTER_TESTING_BUILDER(CornellBoxPhotometric, cornell_box_photometric); } // namespace testing } // namespace ospray diff --git a/apps/common/ospray_testing/builders/Curves.cpp b/apps/common/ospray_testing/builders/Curves.cpp index 2752874309..6d05ebf894 100644 --- a/apps/common/ospray_testing/builders/Curves.cpp +++ b/apps/common/ospray_testing/builders/Curves.cpp @@ -1,4 +1,4 @@ -// Copyright 2009-2019 Intel Corporation +// Copyright 2009-2020 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include @@ -52,8 +52,8 @@ cpp::Group Curves::buildGroup() const cpp::Geometry geom("curve"); if (curveBasis == "hermite") { - geom.setParam("type", int(OSP_ROUND)); - geom.setParam("basis", int(OSP_HERMITE)); + geom.setParam("type", OSP_ROUND); + geom.setParam("basis", OSP_HERMITE); std::vector tangents; for (auto iter = points.begin(); iter != points.end() - 1; ++iter) { const vec4f pointTangent = *(iter + 1) - *iter; @@ -62,16 +62,16 @@ cpp::Group Curves::buildGroup() const geom.setParam("vertex.position_radius", cpp::Data(points)); geom.setParam("vertex.tangent", cpp::Data(tangents)); } else if (curveBasis == "catmull-rom") { - geom.setParam("type", int(OSP_ROUND)); - geom.setParam("basis", int(OSP_CATMULL_ROM)); + geom.setParam("type", OSP_ROUND); + geom.setParam("basis", OSP_CATMULL_ROM); geom.setParam("vertex.position_radius", cpp::Data(points)); } else if (curveBasis == "linear") { geom.setParam("radius", 0.1f); geom.setParam("vertex.position", cpp::Data(points.size(), sizeof(vec4f), (vec3f *)points.data())); } else { - geom.setParam("type", int(OSP_ROUND)); - geom.setParam("basis", int(OSP_BSPLINE)); + geom.setParam("type", OSP_ROUND); + geom.setParam("basis", OSP_BSPLINE); geom.setParam("vertex.position_radius", cpp::Data(points)); } diff --git a/apps/common/ospray_testing/builders/GravitySpheresVolume.cpp b/apps/common/ospray_testing/builders/GravitySpheresVolume.cpp index 8023413417..e5aefc1cd4 100644 --- a/apps/common/ospray_testing/builders/GravitySpheresVolume.cpp +++ b/apps/common/ospray_testing/builders/GravitySpheresVolume.cpp @@ -1,4 +1,4 @@ -// Copyright 2009-2019 Intel Corporation +// Copyright 2009-2020 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "Builder.h" @@ -21,13 +21,16 @@ using VoxelArray = std::vector; struct GravitySpheres : public detail::Builder { - GravitySpheres( - bool addVolume = true, bool asAMR = false, bool addIsosurface = false); + GravitySpheres(bool addVolume = true, + bool asAMR = false, + bool addIsosurface = false, + bool clip = false); ~GravitySpheres() override = default; void commit() override; cpp::Group buildGroup() const override; + cpp::World buildWorld() const override; private: VoxelArray generateVoxels() const; @@ -43,12 +46,17 @@ struct GravitySpheres : public detail::Builder bool createAsAMR{false}; bool withIsosurface{false}; float isovalue{2.5f}; + bool withClipping{false}; }; // Inlined definitions //////////////////////////////////////////////////// -GravitySpheres::GravitySpheres(bool addVolume, bool asAMR, bool addIsosurface) - : withVolume(addVolume), createAsAMR(asAMR), withIsosurface(addIsosurface) +GravitySpheres::GravitySpheres( + bool addVolume, bool asAMR, bool addIsosurface, bool clip) + : withVolume(addVolume), + createAsAMR(asAMR), + withIsosurface(addIsosurface), + withClipping(clip) {} void GravitySpheres::commit() @@ -61,6 +69,7 @@ void GravitySpheres::commit() createAsAMR = getParam("asAMR", createAsAMR); withIsosurface = getParam("withIsosurface", withIsosurface); isovalue = getParam("isovalue", 2.5f); + withClipping = getParam("withClipping", withClipping); addPlane = false; } @@ -108,6 +117,49 @@ cpp::Group GravitySpheres::buildGroup() const return group; } +cpp::World GravitySpheres::buildWorld() const +{ + // Skip clipping if not enabled + std::vector instances; + if (withClipping) { + // Create clipping plane + std::vector geometricModels; + { + cpp::Geometry planeGeometry("plane"); + std::vector coefficients = {vec4f(1.f, -1.f, 1.f, 0.f)}; + planeGeometry.setParam("plane.coefficients", cpp::Data(coefficients)); + planeGeometry.commit(); + + cpp::GeometricModel model(planeGeometry); + model.commit(); + geometricModels.emplace_back(model); + } + + // Create clipping sphere + { + cpp::Geometry sphereGeometry("sphere"); + std::vector position = {vec3f(.2f, -.2f, .2f)}; + sphereGeometry.setParam("sphere.position", cpp::Data(position)); + sphereGeometry.setParam("radius", .5f); + sphereGeometry.commit(); + + cpp::GeometricModel model(sphereGeometry); + model.commit(); + geometricModels.emplace_back(model); + } + + cpp::Group group; + group.setParam("clippingGeometry", cpp::Data(geometricModels)); + group.commit(); + + cpp::Instance inst(group); + inst.commit(); + instances.push_back(inst); + } + + return Builder::buildWorld(instances); +} + std::vector GravitySpheres::generateVoxels() const { struct Point @@ -232,5 +284,8 @@ OSP_REGISTER_TESTING_BUILDER( OSP_REGISTER_TESTING_BUILDER( GravitySpheres(false, false, true), gravity_spheres_isosurface); +OSP_REGISTER_TESTING_BUILDER( + GravitySpheres(true, false, false, true), clip_gravity_spheres_volume); + } // namespace testing } // namespace ospray diff --git a/apps/common/ospray_testing/builders/PerlinNoiseVolumes.cpp b/apps/common/ospray_testing/builders/PerlinNoiseVolumes.cpp index 0be5f464d6..dea6495dc5 100644 --- a/apps/common/ospray_testing/builders/PerlinNoiseVolumes.cpp +++ b/apps/common/ospray_testing/builders/PerlinNoiseVolumes.cpp @@ -1,4 +1,4 @@ -// Copyright 2009-2019 Intel Corporation +// Copyright 2009-2020 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "Builder.h" @@ -15,7 +15,7 @@ namespace testing { struct PerlinNoiseVolumes : public detail::Builder { - PerlinNoiseVolumes() = default; + PerlinNoiseVolumes(bool clip = false); ~PerlinNoiseVolumes() override = default; void commit() override; @@ -33,6 +33,8 @@ struct PerlinNoiseVolumes : public detail::Builder float densityScale{10.f}; float anisotropy{0.f}; + + bool withClipping{false}; }; /* heavily based on Perlin's Java reference implementation of @@ -446,6 +448,8 @@ cpp::GeometricModel createGeometricModel( // PerlineNoiseVolumes definitions // +PerlinNoiseVolumes::PerlinNoiseVolumes(bool clip) : withClipping(clip) {} + void PerlinNoiseVolumes::commit() { Builder::commit(); @@ -459,6 +463,8 @@ void PerlinNoiseVolumes::commit() densityScale = getParam("densityScale", 10.f); anisotropy = getParam("anisotropy", 0.f); + + withClipping = getParam("withClipping", withClipping); } cpp::Group PerlinNoiseVolumes::buildGroup() const @@ -546,7 +552,29 @@ cpp::Group PerlinNoiseVolumes::buildGroup() const cpp::World PerlinNoiseVolumes::buildWorld() const { - auto world = Builder::buildWorld(); + // Skip clipping if not enabled + std::vector instances; + if (withClipping) { + // Create clipping sphere + cpp::Geometry sphereGeometry("sphere"); + std::vector position = {vec3f(-.5f, .2f, -.5f)}; + sphereGeometry.setParam("sphere.position", cpp::Data(position)); + sphereGeometry.setParam("radius", 1.2f); + sphereGeometry.commit(); + + cpp::GeometricModel model(sphereGeometry); + model.commit(); + + cpp::Group group; + group.setParam("clippingGeometry", cpp::Data(model)); + group.commit(); + + cpp::Instance inst(group); + inst.commit(); + instances.push_back(inst); + } + + auto world = Builder::buildWorld(instances); std::vector lightHandles; @@ -578,6 +606,8 @@ cpp::World PerlinNoiseVolumes::buildWorld() const } OSP_REGISTER_TESTING_BUILDER(PerlinNoiseVolumes, perlin_noise_volumes); +OSP_REGISTER_TESTING_BUILDER( + PerlinNoiseVolumes(true), clip_perlin_noise_volumes); } // namespace testing } // namespace ospray diff --git a/apps/common/ospray_testing/builders/Planes.cpp b/apps/common/ospray_testing/builders/Planes.cpp new file mode 100644 index 0000000000..6c8d78071f --- /dev/null +++ b/apps/common/ospray_testing/builders/Planes.cpp @@ -0,0 +1,72 @@ +// Copyright 2009-2020 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#include "Builder.h" +#include "ospray_testing.h" + +using namespace ospcommon::math; + +namespace ospray { +namespace testing { + +struct Planes : public detail::Builder +{ + Planes() = default; + ~Planes() override = default; + + void commit() override; + + cpp::Group buildGroup() const override; +}; + +// Inlined definitions //////////////////////////////////////////////////// + +void Planes::commit() +{ + Builder::commit(); + + addPlane = false; +} + +cpp::Group Planes::buildGroup() const +{ + // Create plane geometry + cpp::Geometry planeGeometry("plane"); + std::vector coeffs; + std::vector bounds; + const box3f b = box3f(vec3f(-2.f, -2.f, -1.f), vec3f(2.f, 2.f, 1.f)); + const uint32_t count = 28; + for (uint32_t i = 0; i < count; i++) { + affine3f rotate = affine3f::rotate( + vec3f(.3f, 0.f, 1.f), (i / float(count)) * 2.0f * M_PI); + vec3f p = xfmNormal(rotate, vec3f(1.f, 0.f, 0.f)); + coeffs.push_back(vec4f(p.x, p.y, p.z, 0.f)); + bounds.push_back(b); + } + planeGeometry.setParam("plane.coefficients", cpp::Data(coeffs)); + planeGeometry.setParam("plane.bounds", cpp::Data(bounds)); + planeGeometry.commit(); + + // Create geometric model + cpp::GeometricModel model(planeGeometry); + if (rendererType == "pathtracer" || rendererType == "scivis") { + cpp::Material material(rendererType, "obj"); + material.setParam("kd", vec3f(.1f, .4f, .8f)); + material.commit(); + model.setParam("material", material); + } + model.commit(); + + // Create group + cpp::Group group; + group.setParam("geometry", cpp::Data(model)); + group.commit(); + + // Done + return group; +} + +OSP_REGISTER_TESTING_BUILDER(Planes, planes); + +} // namespace testing +} // namespace ospray diff --git a/apps/common/ospray_testing/builders/Streamlines.cpp b/apps/common/ospray_testing/builders/Streamlines.cpp index c9189facca..0acce8eefa 100644 --- a/apps/common/ospray_testing/builders/Streamlines.cpp +++ b/apps/common/ospray_testing/builders/Streamlines.cpp @@ -1,4 +1,4 @@ -// Copyright 2009-2019 Intel Corporation +// Copyright 2009-2020 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "Builder.h" @@ -106,8 +106,8 @@ cpp::Group Streamlines::buildGroup() const slGeom.setParam("vertex.position_radius", cpp::Data(points)); slGeom.setParam("index", cpp::Data(indices)); slGeom.setParam("vertex.color", cpp::Data(colors)); - slGeom.setParam("type", int(OSP_ROUND)); - slGeom.setParam("basis", int(OSP_CATMULL_ROM)); + slGeom.setParam("type", OSP_ROUND); + slGeom.setParam("basis", OSP_CATMULL_ROM); slGeom.commit(); diff --git a/apps/common/ospray_testing/builders/SubdivisionCube.cpp b/apps/common/ospray_testing/builders/SubdivisionCube.cpp index 74d33f88df..6f9579fff2 100644 --- a/apps/common/ospray_testing/builders/SubdivisionCube.cpp +++ b/apps/common/ospray_testing/builders/SubdivisionCube.cpp @@ -1,4 +1,4 @@ -// Copyright 2009-2019 Intel Corporation +// Copyright 2009-2020 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "Builder.h" @@ -108,7 +108,7 @@ cpp::Group SubdivisionCube::buildGroup() const geometry.setParam("edgeCrease.index", cpp::Data(edgeCreaseIndices)); geometry.setParam("edgeCrease.weight", cpp::Data(edgeCreaseWeights)); geometry.setParam("level", level); - geometry.setParam("mode", (int)OSP_SUBDIVISION_PIN_CORNERS); + geometry.setParam("mode", OSP_SUBDIVISION_PIN_CORNERS); geometry.commit(); diff --git a/apps/ospBenchmark/CMakeLists.txt b/apps/ospBenchmark/CMakeLists.txt index 412742e0ed..02447dddd0 100644 --- a/apps/ospBenchmark/CMakeLists.txt +++ b/apps/ospBenchmark/CMakeLists.txt @@ -1,4 +1,4 @@ -## Copyright 2018-2019 Intel Corporation +## Copyright 2018-2020 Intel Corporation ## SPDX-License-Identifier: Apache-2.0 if (NOT OSPRAY_APPS_TESTING) @@ -6,6 +6,8 @@ if (NOT OSPRAY_APPS_TESTING) endif() add_executable(ospBenchmark + ${OSPRAY_RESOURCE} + ospBenchmark.cpp BaseFixture.cpp diff --git a/apps/ospBenchmark/benchmarks/CornellBoxSpp.cpp b/apps/ospBenchmark/benchmarks/CornellBoxSpp.cpp index 9b897e0363..15b88e9514 100644 --- a/apps/ospBenchmark/benchmarks/CornellBoxSpp.cpp +++ b/apps/ospBenchmark/benchmarks/CornellBoxSpp.cpp @@ -1,18 +1,5 @@ -// ======================================================================== // -// Copyright 2018-2019 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file excepathtracer in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // +// Copyright 2018-2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 #include "../BaseFixture.h" diff --git a/apps/ospBenchmark/benchmarks/GravitySpheresVolume.cpp b/apps/ospBenchmark/benchmarks/GravitySpheresVolume.cpp index 2fb7be3ea4..c53b007481 100644 --- a/apps/ospBenchmark/benchmarks/GravitySpheresVolume.cpp +++ b/apps/ospBenchmark/benchmarks/GravitySpheresVolume.cpp @@ -1,18 +1,5 @@ -// ======================================================================== // -// Copyright 2018-2019 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file excepathtracer in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // +// Copyright 2018-2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 #include "../BaseFixture.h" diff --git a/apps/ospBenchmark/benchmarks/PerlinNoiseVolumes.cpp b/apps/ospBenchmark/benchmarks/PerlinNoiseVolumes.cpp index 35f4559ee3..9a37a857dc 100644 --- a/apps/ospBenchmark/benchmarks/PerlinNoiseVolumes.cpp +++ b/apps/ospBenchmark/benchmarks/PerlinNoiseVolumes.cpp @@ -1,18 +1,5 @@ -// ======================================================================== // -// Copyright 2018-2019 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file excepathtracer in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // +// Copyright 2018-2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 #include "../BaseFixture.h" diff --git a/apps/ospExamples/CMakeLists.txt b/apps/ospExamples/CMakeLists.txt index cc1a456c79..a1385d3625 100644 --- a/apps/ospExamples/CMakeLists.txt +++ b/apps/ospExamples/CMakeLists.txt @@ -1,4 +1,4 @@ -## Copyright 2018-2019 Intel Corporation +## Copyright 2018-2020 Intel Corporation ## SPDX-License-Identifier: Apache-2.0 if (NOT OSPRAY_APPS_EXAMPLES) @@ -11,6 +11,7 @@ find_package(OpenGL 2 REQUIRED) find_package(glfw3 REQUIRED) add_executable(ospExamples + ${OSPRAY_RESOURCE} GLFWOSPRayWindow.cpp imgui_impl_glfw_gl3.cpp ospExample.cpp diff --git a/apps/ospExamples/GLFWOSPRayWindow.cpp b/apps/ospExamples/GLFWOSPRayWindow.cpp index af41beed12..c83097b2f9 100644 --- a/apps/ospExamples/GLFWOSPRayWindow.cpp +++ b/apps/ospExamples/GLFWOSPRayWindow.cpp @@ -13,13 +13,13 @@ // on Windows often only GL 1.1 headers are present #ifndef GL_CLAMP_TO_BORDER -#define GL_CLAMP_TO_BORDER 0x812D +#define GL_CLAMP_TO_BORDER 0x812D #endif #ifndef GL_SRGB_ALPHA -#define GL_SRGB_ALPHA 0x8C42 +#define GL_SRGB_ALPHA 0x8C42 #endif #ifndef GL_FRAMEBUFFER_SRGB -#define GL_FRAMEBUFFER_SRGB 0x8DB9 +#define GL_FRAMEBUFFER_SRGB 0x8DB9 #endif static bool g_quitNextFrame = false; @@ -33,7 +33,12 @@ static const std::vector g_scenes = {"boxes", "random_spheres", "streamlines", "subdivision_cube", - "unstructured_volume"}; + "unstructured_volume", + "planes", + "clip_with_spheres", + "clip_with_planes", + "clip_gravity_spheres_volume", + "clip_perlin_noise_volumes"}; static const std::vector g_curveBasis = { "bspline", "hermite", "catmull-rom", "linear"}; @@ -87,7 +92,7 @@ void error_callback(int error, const char *desc) GLFWOSPRayWindow *GLFWOSPRayWindow::activeWindow = nullptr; GLFWOSPRayWindow::GLFWOSPRayWindow(const vec2i &windowSize, bool denoiser) - : denoiserAvailable(denoiser) + : denoiserAvailable(denoiser) { if (activeWindow != nullptr) { throw std::runtime_error("Cannot create more than one GLFWOSPRayWindow!"); @@ -218,15 +223,11 @@ void GLFWOSPRayWindow::reshape(const vec2i &newWindowSize) windowSize = newWindowSize; // create new frame buffer - auto buffers = OSP_FB_COLOR | OSP_FB_DEPTH | OSP_FB_ACCUM | OSP_FB_ALBEDO; - if (denoiserEnabled) - buffers |= OSP_FB_NORMAL; + auto buffers = OSP_FB_COLOR | OSP_FB_DEPTH | OSP_FB_ACCUM | OSP_FB_ALBEDO + | OSP_FB_NORMAL; framebuffer = cpp::FrameBuffer(windowSize, OSP_FB_RGBA32F, buffers); - if (denoiserEnabled) { - cpp::ImageOperation d("denoiser"); - framebuffer.setParam("imageOperation", cpp::Data(d)); - } - framebuffer.commit(); + + refreshFrameOperations(); // reset OpenGL viewport and orthographic projection glViewport(0, 0, windowSize.x, windowSize.y); @@ -291,8 +292,6 @@ void GLFWOSPRayWindow::motion(const vec2f &position) void GLFWOSPRayWindow::display() { - static auto displayStart = std::chrono::high_resolution_clock::now(); - if (showUi) buildUI(); @@ -306,19 +305,10 @@ void GLFWOSPRayWindow::display() static bool firstFrame = true; if (firstFrame || currentFrame.isReady()) { - // display frame rate in window title - auto displayEnd = std::chrono::high_resolution_clock::now(); - auto durationMilliseconds = - std::chrono::duration_cast( - displayEnd - displayStart); - - latestFPS = 1000.f / float(durationMilliseconds.count()); - - // map OSPRay frame buffer, update OpenGL texture with its contents, then - // unmap - waitOnOSPRayFrame(); + latestFPS = 1.f / currentFrame.duration(); + auto *fb = framebuffer.map(showAlbedo ? OSP_FB_ALBEDO : OSP_FB_COLOR); const GLint glFormat = showAlbedo ? GL_RGB : GL_RGBA; @@ -337,8 +327,6 @@ void GLFWOSPRayWindow::display() commitOutstandingHandles(); - // Start new frame and reset frame timing interval start - displayStart = std::chrono::high_resolution_clock::now(); startNewOSPRayFrame(); firstFrame = false; } @@ -376,6 +364,10 @@ void GLFWOSPRayWindow::display() void GLFWOSPRayWindow::startNewOSPRayFrame() { + if (updateFrameOpsNextFrame) { + refreshFrameOperations(); + updateFrameOpsNextFrame = false; + } currentFrame = framebuffer.renderFrame(renderer, camera, world); } @@ -477,8 +469,8 @@ void GLFWOSPRayWindow::buildUI() ImGui::Checkbox("cancel frame on interaction", &cancelFrameOnInteraction); ImGui::Checkbox("show albedo", &showAlbedo); if (denoiserAvailable) { - if(ImGui::Checkbox("denoiser", &denoiserEnabled)) - reshape(this->windowSize); + if (ImGui::Checkbox("denoiser", &denoiserEnabled)) + updateFrameOpsNextFrame = true; } ImGui::Separator(); @@ -494,7 +486,40 @@ void GLFWOSPRayWindow::buildUI() addObjectToCommit(renderer.handle()); } + static bool useTestTex = false; + if (ImGui::Checkbox("backplate texture", &useTestTex)) { + if (useTestTex) { + renderer.setParam("map_backplate", backplateTex); + } else { + renderer.removeParam("map_backplate"); + } + addObjectToCommit(renderer.handle()); + } + if (rendererType == OSPRayRendererType::PATHTRACER) { + if (ImGui::Checkbox("renderSunSky", &renderSunSky)) { + if (renderSunSky) { + sunSky.setParam("direction", sunDirection); + world.setParam("light", cpp::Data(sunSky)); + addObjectToCommit(sunSky.handle()); + } else { + cpp::Light light("ambient"); + light.setParam("visible", false); + light.commit(); + world.setParam("light", cpp::Data(light)); + } + addObjectToCommit(world.handle()); + } + if (renderSunSky) { + if (ImGui::DragFloat3("sunDirection", sunDirection, 0.01f, -1.f, 1.f)) { + sunSky.setParam("direction", sunDirection); + addObjectToCommit(sunSky.handle()); + } + if (ImGui::DragFloat("turbidity", &turbidity, 0.1f, 1.f, 10.f)) { + sunSky.setParam("turbidity", turbidity); + addObjectToCommit(sunSky.handle()); + } + } static int maxDepth = 20; if (ImGui::SliderInt("maxPathLength", &maxDepth, 1, 64)) { renderer.setParam("maxPathLength", maxDepth); @@ -513,16 +538,6 @@ void GLFWOSPRayWindow::buildUI() addObjectToCommit(renderer.handle()); } } else if (rendererType == OSPRayRendererType::SCIVIS) { - static bool useTestTex = false; - if (ImGui::Checkbox("backplate texture", &useTestTex)) { - if (useTestTex) { - renderer.setParam("map_backplate", backplateTex); - } else { - renderer.removeParam("map_backplate"); - } - addObjectToCommit(renderer.handle()); - } - static int aoSamples = 1; if (ImGui::SliderInt("aoSamples", &aoSamples, 0, 64)) { renderer.setParam("aoSamples", aoSamples); @@ -562,6 +577,7 @@ void GLFWOSPRayWindow::commitOutstandingHandles() void GLFWOSPRayWindow::refreshScene(bool resetCamera) { + renderSunSky = false; auto builder = testing::newBuilder(scene); testing::setParam(builder, "rendererType", rendererTypeStr); if (scene == "curves") { @@ -601,3 +617,15 @@ void GLFWOSPRayWindow::refreshScene(bool resetCamera) camera.commit(); } } + +void GLFWOSPRayWindow::refreshFrameOperations() +{ + if (denoiserEnabled) { + cpp::ImageOperation d("denoiser"); + framebuffer.setParam("imageOperation", cpp::Data(d)); + } else { + framebuffer.removeParam("imageOperation"); + } + + framebuffer.commit(); +} diff --git a/apps/ospExamples/GLFWOSPRayWindow.h b/apps/ospExamples/GLFWOSPRayWindow.h index de19f79c51..5d78604285 100644 --- a/apps/ospExamples/GLFWOSPRayWindow.h +++ b/apps/ospExamples/GLFWOSPRayWindow.h @@ -49,6 +49,7 @@ class GLFWOSPRayWindow void buildUI(); void commitOutstandingHandles(); void refreshScene(bool resetCamera = false); + void refreshFrameOperations(); static GLFWOSPRayWindow *activeWindow; @@ -56,8 +57,10 @@ class GLFWOSPRayWindow vec2f previousMouse{-1.f}; bool denoiserAvailable{false}; + bool updateFrameOpsNextFrame{false}; bool denoiserEnabled{false}; bool showAlbedo{false}; + bool renderSunSky{false}; bool cancelFrameOnInteraction{false}; // GLFW window instance @@ -70,11 +73,14 @@ class GLFWOSPRayWindow cpp::Renderer renderer; cpp::Camera camera{"perspective"}; cpp::World world; + cpp::Light sunSky{"sunSky"}; cpp::FrameBuffer framebuffer; cpp::Future currentFrame; cpp::Texture backplateTex{"texture2d"}; vec3f bgColor{0.f}; + vec3f sunDirection{-0.25f, -1.0f, 0.0f}; + float turbidity{3.f}; std::string scene{"boxes"}; diff --git a/apps/ospExamples/example_common.h b/apps/ospExamples/example_common.h index 60761b7366..034cfa2486 100644 --- a/apps/ospExamples/example_common.h +++ b/apps/ospExamples/example_common.h @@ -1,4 +1,4 @@ -// Copyright 2018-2019 Intel Corporation +// Copyright 2018-2020 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once @@ -44,4 +44,5 @@ inline void initializeOSPRay( ospDeviceSetParam(device, "logLevel", OSP_INT, &logLevel); ospDeviceCommit(device); + ospDeviceRelease(device); } diff --git a/apps/ospTestSuite/CMakeLists.txt b/apps/ospTestSuite/CMakeLists.txt index 59d37816c5..adc96df923 100644 --- a/apps/ospTestSuite/CMakeLists.txt +++ b/apps/ospTestSuite/CMakeLists.txt @@ -1,4 +1,4 @@ -## Copyright 2017-2019 Intel Corporation +## Copyright 2017-2020 Intel Corporation ## SPDX-License-Identifier: Apache-2.0 if (NOT OSPRAY_APPS_TESTING) @@ -8,11 +8,14 @@ endif() ospray_disable_compiler_warnings() add_executable(ospTestSuite + ${OSPRAY_RESOURCE} environment.cpp test_fixture.cpp - test_geometry.cpp test_tools.cpp + test_geometry.cpp test_volumetric.cpp + test_appearance.cpp + test_light.cpp test_enums.cpp ospTestSuite.cpp ) diff --git a/apps/ospTestSuite/environment.cpp b/apps/ospTestSuite/environment.cpp index eacb964ff0..aa21bac953 100644 --- a/apps/ospTestSuite/environment.cpp +++ b/apps/ospTestSuite/environment.cpp @@ -6,34 +6,10 @@ OSPRayEnvironment::OSPRayEnvironment(int argc, char **argv) : dumpImg(false), rendererType("scivis"), - deviceType("cpu"), baselineDir("regression_test_baseline"), failedDir("failed") { ParsArgs(argc, argv); - ospLoadModule("ispc"); - device = ospNewDevice(GetDeviceType().c_str()); - if (device == NULL) { - std::cout << "Failed to init the ospray device" << std::endl << std::flush; - std::exit(EXIT_FAILURE); - } - - ospDeviceSetErrorFunc(device, [](OSPError error, const char *errorDetails) { - std::cerr << "OSPRay error: " << errorDetails << std::endl; - std::exit(EXIT_FAILURE); - }); - - ospDeviceSetStatusFunc(device, [](const char *msg) { std::cout << msg; }); - - bool warnAsErrors = true; - auto logLevel = OSP_LOG_WARNING; - - ospDeviceSetParam(device, "warnAsError", OSP_BOOL, &warnAsErrors); - ospDeviceSetParam(device, "logLevel", OSP_INT, &logLevel); - - ospDeviceCommit(device); - ospSetCurrentDevice(device); - ospDeviceRelease(device); } void OSPRayEnvironment::ParsArgs(int argc, char **argv) @@ -48,8 +24,6 @@ void OSPRayEnvironment::ParsArgs(int argc, char **argv) << "--dump-img : dump the rendered image to file\n" << "--imgsize-x=XX : change the length of an image\n" << "--imgsize-y=XX : change the height of an image\n" - << "--device-type=XX : change the device type which is used by " - "OSPRay\n" << "--renderer-type=XX : change the renderer used for tests\n" << "--baseline-dir=XX : Change the directory used for baseline " "images during tests\n"; @@ -62,8 +36,6 @@ void OSPRayEnvironment::ParsArgs(int argc, char **argv) imgSize.x = GetNumArgValue(&testArgs.at(idx)); } else if (testArgs.at(idx).find("--imgsize-y=") == 0) { imgSize.y = GetNumArgValue(&testArgs.at(idx)); - } else if (testArgs.at(idx).find("--device-type=") == 0) { - deviceType = GetStrArgValue(&testArgs.at(idx)); } else if (testArgs.at(idx).find("--baseline-dir=") == 0) { baselineDir = GetStrArgValue(&testArgs.at(idx)); } else if (testArgs.at(idx).find("--failed-dir=") == 0) { diff --git a/apps/ospTestSuite/environment.h b/apps/ospTestSuite/environment.h index 851d410c1b..9334c4270e 100644 --- a/apps/ospTestSuite/environment.h +++ b/apps/ospTestSuite/environment.h @@ -16,15 +16,6 @@ using namespace ospcommon::math; class OSPRayEnvironment : public ::testing::Environment { - private: - bool dumpImg; - std::string rendererType; - std::string deviceType; - std::string baselineDir; - std::string failedDir; - vec2i imgSize{1024, 768}; - OSPDevice device{nullptr}; - public: OSPRayEnvironment(int argc, char **argv); ~OSPRayEnvironment() = default; @@ -41,10 +32,6 @@ class OSPRayEnvironment : public ::testing::Environment { return imgSize; } - std::string GetDeviceType() const - { - return deviceType; - } std::string GetBaselineDir() const { return baselineDir; @@ -57,4 +44,11 @@ class OSPRayEnvironment : public ::testing::Environment void ParsArgs(int argc, char **argv); std::string GetStrArgValue(std::string *arg) const; int GetNumArgValue(std::string *arg) const; + + private: + bool dumpImg; + std::string rendererType; + std::string baselineDir; + std::string failedDir; + vec2i imgSize{1024, 768}; }; diff --git a/apps/ospTestSuite/ospTestSuite.cpp b/apps/ospTestSuite/ospTestSuite.cpp index 8f6847db09..cd6185741e 100644 --- a/apps/ospTestSuite/ospTestSuite.cpp +++ b/apps/ospTestSuite/ospTestSuite.cpp @@ -1,4 +1,4 @@ -// Copyright 2017-2019 Intel Corporation +// Copyright 2017-2020 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "test_fixture.h" @@ -8,10 +8,30 @@ OSPRayEnvironment *ospEnv; int main(int argc, char **argv) { + ospInit(&argc, (const char **)argv); + + { + cpp::Device device = cpp::Device::current(); + + device.setErrorFunc([](OSPError error, const char *errorDetails) { + std::cerr << "OSPRay error: " << errorDetails << std::endl; + std::exit(EXIT_FAILURE); + }); + + device.setStatusFunc([](const char *msg) { std::cout << msg; }); + + device.setParam("warnAsError", true); + device.setParam("logLevel", OSP_LOG_WARNING); + + device.commit(); + } + ::testing::InitGoogleTest(&argc, argv); ospEnv = new OSPRayEnvironment(argc, argv); AddGlobalTestEnvironment(ospEnv); auto result = RUN_ALL_TESTS(); + ospShutdown(); + return result; } diff --git a/apps/ospTestSuite/test_appearance.cpp b/apps/ospTestSuite/test_appearance.cpp new file mode 100644 index 0000000000..7bf1232ae0 --- /dev/null +++ b/apps/ospTestSuite/test_appearance.cpp @@ -0,0 +1,282 @@ +// Copyright 2020 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#include "test_appearance.h" +#include "ArcballCamera.h" +#include "ospcommon/utility/multidim_index_sequence.h" + +namespace OSPRayTestScenes { + +Texture2D::Texture2D() +{ + rendererType = "pathtracer"; + samplesPerPixel = 64; +} + +void Texture2D::SetUp() +{ + Base::SetUp(); + camera.setParam("position", vec3f(4.f, 3.f, 0.f)); + // flip image to have origin in upper left corner, plus mirror + camera.setParam("imageStart", vec2f(1.f, 1.f)); + camera.setParam("imageEnd", vec2f(0.f, 0.f)); + auto params = GetParam(); + int filter = std::get<0>(params); + + // create (4*2) x 4 grid + constexpr int cols = 8; + constexpr int rows = 4; + std::array vertex; + ospcommon::index_sequence_2D vidx(vec2i(cols + 1, rows + 1)); + for (auto i : vidx) + vertex[vidx.flatten(i)] = vec3f(i.x, i.y * 1.5f, 5.3f); + std::array index; + ospcommon::index_sequence_2D iidx(vec2i(cols, rows)); + for (auto i : iidx) { + index[iidx.flatten(i)] = vec4i(vidx.flatten({i.x, i.y}), + vidx.flatten({i.x + 1, i.y}), + vidx.flatten({i.x + 1, i.y + 1}), + vidx.flatten({i.x, i.y + 1})); + } + cpp::Geometry mesh("mesh"); + mesh.setParam("vertex.position", cpp::Data(vertex)); + mesh.setParam("index", cpp::Data(index)); + mesh.commit(); + + // create textures: columns=#channels, rows=type=[byte, byte, float, short] + std::array format = {OSP_TEXTURE_R8, + OSP_TEXTURE_RA8, + OSP_TEXTURE_RGB8, + OSP_TEXTURE_RGBA8, + + OSP_TEXTURE_L8, + OSP_TEXTURE_LA8, + OSP_TEXTURE_SRGB, + OSP_TEXTURE_SRGBA, + + OSP_TEXTURE_R32F, + OSP_TEXTURE_R32F, + OSP_TEXTURE_RGB32F, + OSP_TEXTURE_RGBA32F, + + OSP_TEXTURE_R16, + OSP_TEXTURE_RA16, + OSP_TEXTURE_RGB16, + OSP_TEXTURE_RGBA16}; + + std::array eltype = {OSP_UCHAR, + OSP_VEC2UC, + OSP_VEC3UC, + OSP_VEC4UC, + + OSP_UCHAR, + OSP_VEC2UC, + OSP_VEC3UC, + OSP_VEC4UC, + + OSP_FLOAT, + OSP_FLOAT, + OSP_VEC3F, + OSP_VEC4F, + + OSP_USHORT, + OSP_VEC2US, + OSP_VEC3US, + OSP_VEC4US}; + + std::array dbyte; + std::array dshort; + std::array dfloat; + ospcommon::index_sequence_2D didx(vec2i(3, 5)); + for (auto idx : didx) { + auto i = didx.flatten(idx); + // the center texel should be 127 / 32767 / 0.5, to test normal maps + dbyte[i] = vec4uc(idx.x * 85 + 42, idx.y * 50 + 27, 155, i * 17 + 8); + dshort[i] = vec4us( + idx.x * 22768 + 9999, idx.y * 13000 + 6767, 33000, i * 4400 + 2200); + dfloat[i] = vec4f(idx.x * 0.3125f + 0.1875f, + idx.y * 0.21875f + 0.0625f, + 0.8f, + i * 0.06f + 0.15f); + } + + std::array addr = { + dbyte.data(), dbyte.data(), dfloat.data(), dshort.data()}; + std::array stride = {4, 4, 16, 8}; + + cpp::GeometricModel model(mesh); + std::array material; + for (auto idx : iidx) { + auto i = iidx.flatten(idx); + auto fmt = format[i / 2]; + auto eltp = eltype[i / 2]; + cpp::Texture tex("texture2d"); + tex.setParam("format", fmt); + tex.setParam("filter", filter); + auto tmp = ospNewSharedData(addr[idx.y], eltp, 3, stride[idx.y], 5); + auto data = ospNewData(eltp, 3, 5); + ospCopyData(tmp, data); + ospRelease(tmp); + tex.setParam("data", data); + tex.commit(); + cpp::Material mat(rendererType, "obj"); + mat.setParam(i & 1 ? "map_bump" : "map_kd", tex); + mat.commit(); + material[i] = mat; + } + model.setParam("material", cpp::Data(material)); + AddModel(model); + + { // set up backplate texture + std::array bpdata; + bool toggle = false; + for (auto &el : bpdata) { + el = toggle ? vec3uc(5, 10, 5) : vec3uc(10, 5, 10); + toggle = !toggle; + } + cpp::Data texData(vec2ul(125, 94), bpdata.data()); + + cpp::Texture backplateTex("texture2d"); + backplateTex.setParam("format", OSP_TEXTURE_RGB8); + backplateTex.setParam("filter", OSP_TEXTURE_FILTER_NEAREST); + backplateTex.setParam("data", texData); + backplateTex.commit(); + + renderer.setParam("map_backplate", backplateTex); + } + + cpp::Light light1("distant"); + cpp::Light light2("distant"); + + // two light sets + if (std::get<1>(params)) { + // default light + light1.setParam("intensity", 3.f); + // highlighting normal direction + light2.setParam("direction", vec3f(0.7f, 0.2f, 0.f)); + light2.setParam("color", vec3f(0.f, 0.f, 0.5f)); + } else { + // perpendicular bright lights, testing center normal + light1.setParam("direction", vec3f(0.5f, -1.f, 0.f)); + light1.setParam("color", vec3f(999.f, 0.f, 0.0f)); + light2.setParam("direction", vec3f(-0.5f, 1.f, 0.f)); + light2.setParam("color", vec3f(0.f, 999.f, 0.0f)); + } + + AddLight(light1); + AddLight(light2); +} + +RendererMaterialList::RendererMaterialList() +{ + rendererType = GetParam(); +} + +void RendererMaterialList::SetUp() +{ + Base::SetUp(); + + // Setup geometry // + + cpp::Geometry sphereGeometry("sphere"); + + constexpr int dimSize = 3; + + ospcommon::index_sequence_2D numSpheres(dimSize); + + std::vector spheres; + std::vector index; + std::vector materials; + + auto makeObjMaterial = + [](const std::string &rendererType, vec3f Kd, vec3f Ks) -> cpp::Material { + cpp::Material mat(rendererType, "obj"); + mat.setParam("kd", Kd); + if (rendererType == "pathtracer") + mat.setParam("ks", Ks); + mat.commit(); + + return mat; + }; + + for (auto i : numSpheres) { + auto i_f = static_cast(i); + spheres.emplace_back(i_f.x, i_f.y, 0.f); + + auto l = i_f / (dimSize - 1); + materials.push_back(makeObjMaterial(rendererType, + lerp(l.x, vec3f(0.1f), vec3f(0.f, 0.f, 1.f)), + lerp(l.y, vec3f(0.f), vec3f(1.f)))); + + index.push_back(static_cast(numSpheres.flatten(i))); + } + + sphereGeometry.setParam("sphere.position", cpp::Data(spheres)); + sphereGeometry.setParam("radius", 0.4f); + sphereGeometry.commit(); + + cpp::GeometricModel model(sphereGeometry); + model.setParam("material", cpp::Data(index)); + + AddModel(model); + + // Setup renderer material list // + + renderer.setParam("material", cpp::Data(materials)); + + // Create the world to get bounds for camera setup // + + if (!instances.empty()) + world.setParam("instance", cpp::Data(instances)); + + instances.clear(); + + world.commit(); + + auto worldBounds = world.getBounds(); + + ArcballCamera arcballCamera(worldBounds, imgSize); + + camera.setParam("position", arcballCamera.eyePos()); + camera.setParam("direction", arcballCamera.lookDir()); + camera.setParam("up", arcballCamera.upDir()); + + // Setup lights // + + cpp::Light ambient("ambient"); + ambient.setParam("intensity", 0.5f); + AddLight(ambient); +} + +// Test Instantiations ////////////////////////////////////////////////////// + +TEST_P(RendererMaterialList, material_list) +{ + PerformRenderTest(); +} + +INSTANTIATE_TEST_SUITE_P(MaterialLists, + RendererMaterialList, + ::testing::Values("scivis", "pathtracer")); + +INSTANTIATE_TEST_SUITE_P(TestScenesPtMaterials, + FromOsprayTesting, + ::testing::Combine(::testing::Values("test_pt_glass", + "test_pt_luminous", + "test_pt_metal_roughness", + "test_pt_metallic_flakes", + "test_pt_obj"), + ::testing::Values("pathtracer"))); + +TEST_P(Texture2D, filter) +{ + PerformRenderTest(); +} + +INSTANTIATE_TEST_SUITE_P(Appearance, + Texture2D, + ::testing::Values(std::make_tuple(OSP_TEXTURE_FILTER_BILINEAR, true), + std::make_tuple(OSP_TEXTURE_FILTER_NEAREST, true), + std::make_tuple(OSP_TEXTURE_FILTER_NEAREST, false))); + +} // namespace OSPRayTestScenes diff --git a/apps/ospTestSuite/test_appearance.h b/apps/ospTestSuite/test_appearance.h new file mode 100644 index 0000000000..36469f5a9e --- /dev/null +++ b/apps/ospTestSuite/test_appearance.h @@ -0,0 +1,26 @@ +// Copyright 2020 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#include "test_fixture.h" + +namespace OSPRayTestScenes { + +// Test all texture image formats (and filter modes) +class Texture2D : public Base, + public ::testing::TestWithParam< + std::tuple> +{ + public: + Texture2D(); + void SetUp() override; +}; + +class RendererMaterialList : public Base, + public ::testing::TestWithParam +{ + public: + RendererMaterialList(); + void SetUp() override; +}; + +} // namespace OSPRayTestScenes diff --git a/apps/ospTestSuite/test_enums.cpp b/apps/ospTestSuite/test_enums.cpp index a6ece4ee9d..a8a979b44a 100644 --- a/apps/ospTestSuite/test_enums.cpp +++ b/apps/ospTestSuite/test_enums.cpp @@ -13,7 +13,7 @@ TEST(Enums, VKLLogLevel) ASSERT_EQ(OSP_LOG_INFO, VKL_LOG_INFO); ASSERT_EQ(OSP_LOG_WARNING, VKL_LOG_WARNING); ASSERT_EQ(OSP_LOG_ERROR, VKL_LOG_ERROR); - // ASSERT_EQ(OSP_LOG_NONE, VKL_LOG_NONE); + ASSERT_EQ(OSP_LOG_NONE, VKL_LOG_NONE); } TEST(Enums, VKLDataType) diff --git a/apps/ospTestSuite/test_fixture.cpp b/apps/ospTestSuite/test_fixture.cpp index b3fd00ddf5..45bb29278d 100644 --- a/apps/ospTestSuite/test_fixture.cpp +++ b/apps/ospTestSuite/test_fixture.cpp @@ -1,52 +1,14 @@ -// Copyright 2017-2019 Intel Corporation +// Copyright 2017-2020 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "test_fixture.h" #include "ArcballCamera.h" -// ospray_testing #include "ospray_testing.h" -// ospcommon -#include "ospcommon/utility/multidim_index_sequence.h" extern OSPRayEnvironment *ospEnv; namespace OSPRayTestScenes { -// Helper functions ///////////////////////////////////////////////////////// - -// creates a torus -// volumetric data: stores data of torus -// returns created ospvolume of torus -static cpp::Volume CreateTorus(const int size) -{ - std::vector volumetricData(size * size * size, 0); - - const float r = 30; - const float R = 80; - const int size_2 = size / 2; - - for (int x = 0; x < size; ++x) { - for (int y = 0; y < size; ++y) { - for (int z = 0; z < size; ++z) { - const float X = x - size_2; - const float Y = y - size_2; - const float Z = z - size_2; - - const float d = (R - std::sqrt(X * X + Y * Y)); - volumetricData[size * size * x + size * y + z] = r * r - d * d - Z * Z; - } - } - } - - cpp::Volume torus("structured_regular"); - torus.setParam("data", cpp::Data(vec3ul(size), volumetricData.data())); - torus.setParam("gridOrigin", vec3f(-0.5f, -0.5f, -0.5f)); - torus.setParam("gridSpacing", vec3f(1.f / size, 1.f / size, 1.f / size)); - return torus; -} - -///////////////////////////////////////////////////////////////////////////// - Base::Base() { const ::testing::TestCase *const testCase = @@ -182,288 +144,6 @@ void Base::RenderFrame() framebuffer.renderFrame(renderer, camera, world); } -SpherePrecision::SpherePrecision() -{ - auto params = GetParam(); - radius = std::get<0>(params); - dist = std::get<1>(params) * radius; - move_cam = std::get<2>(params); - rendererType = std::get<3>(params); -} - -void SpherePrecision::SetUp() -{ - Base::SetUp(); - - float fov = 160.0f * std::min(std::tan(radius / std::abs(dist)), 1.0f); - float cent = move_cam ? 0.0f : dist + radius; - - camera.setParam( - "position", vec3f(0.f, 0.f, move_cam ? -dist - radius : 0.0f)); - camera.setParam("direction", vec3f(0.f, 0.f, 1.f)); - camera.setParam("up", vec3f(0.f, 1.f, 0.f)); - camera.setParam("fovy", fov); - - renderer.setParam("pixelSamples", 16); - renderer.setParam("backgroundColor", vec4f(0.2f, 0.2f, 0.4f, 1.0f)); - if (rendererType == "scivis") { - renderer.setParam("aoSamples", 16); - renderer.setParam("aoIntensity", 1.f); - } else if (rendererType == "pathtracer") { - renderer.setParam("maxPathLength", 2); - } - - cpp::Geometry sphere("sphere"); - cpp::Geometry inst_sphere("sphere"); - - std::vector sph_center = {vec3f(-0.5f * radius, -0.3f * radius, cent), - vec3f(0.8f * radius, -0.3f * radius, cent), - vec3f(0.8f * radius, 1.5f * radius, cent), - vec3f(0.0f, -10001.3f * radius, cent)}; - - std::vector sph_radius = { - radius, 0.9f * radius, 0.9f * radius, 10000.f * radius}; - - sphere.setParam("sphere.position", cpp::Data(sph_center)); - sphere.setParam("sphere.radius", cpp::Data(sph_radius)); - sphere.commit(); - - inst_sphere.setParam("sphere.position", cpp::Data(vec3f(0.f))); - inst_sphere.setParam("sphere.radius", cpp::Data(90.f * radius)); - inst_sphere.commit(); - - cpp::GeometricModel model1(sphere); - cpp::GeometricModel model2(inst_sphere); - - cpp::Material sphereMaterial(rendererType, "obj"); - sphereMaterial.setParam("d", 1.0f); - sphereMaterial.commit(); - - model1.setParam("material", sphereMaterial); - model2.setParam("material", sphereMaterial); - - affine3f xfm(vec3f(0.01, 0, 0), - vec3f(0, 0.01, 0), - vec3f(0, 0, 0.01), - vec3f(-0.5f * radius, 1.6f * radius, cent)); - - AddModel(model1); - AddModel(model2, xfm); - - cpp::Light distant("distant"); - distant.setParam("intensity", 3.0f); - distant.setParam("direction", vec3f(0.3f, -4.0f, 0.8f)); - distant.setParam("color", vec3f(1.0f, 0.5f, 0.5f)); - distant.setParam("angularDiameter", 1.0f); - AddLight(distant); - - cpp::Light ambient = ospNewLight("ambient"); - ambient.setParam("intensity", 0.1f); - AddLight(ambient); -} - -TextureVolume::TextureVolume() -{ - rendererType = GetParam(); -} - -void TextureVolume::SetUp() -{ - Base::SetUp(); - - camera.setParam("position", vec3f(-0.7f, -1.4f, 0.f)); - camera.setParam("direction", vec3f(0.5f, 1.f, 0.f)); - camera.setParam("up", vec3f(0.f, 0.f, -1.f)); - - cpp::Volume torus = CreateTorus(256); - torus.commit(); - - cpp::VolumetricModel volumetricModel(torus); - - cpp::TransferFunction transferFun("piecewiseLinear"); - transferFun.setParam("valueRange", vec2f(-10000.f, 10000.f)); - - std::vector colors = { - vec3f(1.0f, 0.0f, 0.0f), vec3f(0.0f, 1.0f, 0.0f)}; - - std::vector opacities = {1.0f, 1.0f}; - - transferFun.setParam("color", cpp::Data(colors)); - transferFun.setParam("opacity", cpp::Data(opacities)); - transferFun.commit(); - - volumetricModel.setParam("transferFunction", transferFun); - volumetricModel.commit(); - - cpp::Texture tex("volume"); - tex.setParam("volume", volumetricModel); - tex.commit(); - - cpp::Material sphereMaterial(rendererType, "obj"); - sphereMaterial.setParam("map_kd", tex); - sphereMaterial.commit(); - - cpp::Geometry sphere("sphere"); - sphere.setParam("sphere.position", cpp::Data(vec3f(0.f))); - sphere.setParam("radius", 0.51f); - sphere.commit(); - - cpp::GeometricModel model(sphere); - model.setParam("material", sphereMaterial); - AddModel(model); - - cpp::Light ambient("ambient"); - ambient.setParam("intensity", 0.5f); - AddLight(ambient); -} - -DepthCompositeVolume::DepthCompositeVolume() -{ - auto params = GetParam(); - rendererType = std::get<0>(params); - bgColor = std::get<1>(params); -} - -void DepthCompositeVolume::SetUp() -{ - Base::SetUp(); - - camera.setParam("position", vec3f(0.f, 0.f, 1.f)); - camera.setParam("direction", vec3f(0.f, 0.f, -1.f)); - camera.setParam("up", vec3f(0.f, 1.f, 0.f)); - - cpp::Volume torus = CreateTorus(256); - torus.commit(); - - cpp::VolumetricModel volumetricModel(torus); - - cpp::TransferFunction transferFun("piecewiseLinear"); - transferFun.setParam("valueRange", vec2f(-10000.f, 10000.f)); - - std::vector colors = { - vec3f(1.0f, 0.0f, 0.0f), vec3f(0.0f, 1.0f, 0.0f)}; - - std::vector opacities = {0.05f, 1.0f}; - - transferFun.setParam("color", cpp::Data(colors)); - transferFun.setParam("opacity", cpp::Data(opacities)); - transferFun.commit(); - - volumetricModel.setParam("transferFunction", transferFun); - volumetricModel.commit(); - - AddModel(volumetricModel); - - cpp::Texture depthTex("texture2d"); - - std::vector texData(imgSize.product()); - for (int y = 0; y < imgSize.y; y++) { - for (int x = 0; x < imgSize.x; x++) { - const size_t index = imgSize.x * y + x; - if (x < imgSize.x / 3) { - texData[index] = 999.f; - } else if (x < (imgSize.x * 2) / 3) { - texData[index] = 0.75f; - } else { - texData[index] = 0.00001f; - } - } - } - - depthTex.setParam("format", OSP_TEXTURE_R32F); - depthTex.setParam("filter", OSP_TEXTURE_FILTER_NEAREST); - depthTex.setParam("data", cpp::Data(imgSize, texData.data())); - depthTex.commit(); - - renderer.setParam("map_maxDepth", depthTex); - renderer.setParam("backgroundColor", bgColor); - - cpp::Light ambient("ambient"); - ambient.setParam("intensity", 0.5f); - AddLight(ambient); -} - -RendererMaterialList::RendererMaterialList() -{ - rendererType = GetParam(); -} - -void RendererMaterialList::SetUp() -{ - Base::SetUp(); - - // Setup geometry // - - cpp::Geometry sphereGeometry("sphere"); - - constexpr int dimSize = 3; - - ospcommon::index_sequence_2D numSpheres(dimSize); - - std::vector spheres; - std::vector index; - std::vector materials; - - auto makeObjMaterial = - [](const std::string &rendererType, vec3f Kd, vec3f Ks) -> cpp::Material { - cpp::Material mat(rendererType, "obj"); - mat.setParam("kd", Kd); - if (rendererType == "pathtracer") - mat.setParam("ks", Ks); - mat.commit(); - - return mat; - }; - - for (auto i : numSpheres) { - auto i_f = static_cast(i); - spheres.emplace_back(i_f.x, i_f.y, 0.f); - - auto l = i_f / (dimSize - 1); - materials.push_back(makeObjMaterial(rendererType, - lerp(l.x, vec3f(0.1f), vec3f(0.f, 0.f, 1.f)), - lerp(l.y, vec3f(0.f), vec3f(1.f)))); - - index.push_back(static_cast(numSpheres.flatten(i))); - } - - sphereGeometry.setParam("sphere.position", cpp::Data(spheres)); - sphereGeometry.setParam("radius", 0.4f); - sphereGeometry.commit(); - - cpp::GeometricModel model(sphereGeometry); - model.setParam("material", cpp::Data(index)); - - AddModel(model); - - // Setup renderer material list // - - renderer.setParam("material", cpp::Data(materials)); - - // Create the world to get bounds for camera setup // - - if (!instances.empty()) - world.setParam("instance", cpp::Data(instances)); - - instances.clear(); - - world.commit(); - - auto worldBounds = world.getBounds(); - - ArcballCamera arcballCamera(worldBounds, imgSize); - - camera.setParam("position", arcballCamera.eyePos()); - camera.setParam("direction", arcballCamera.lookDir()); - camera.setParam("up", arcballCamera.upDir()); - - // Setup lights // - - cpp::Light ambient("ambient"); - ambient.setParam("intensity", 0.5f); - AddLight(ambient); -} - FromOsprayTesting::FromOsprayTesting() { auto params = GetParam(); diff --git a/apps/ospTestSuite/test_fixture.h b/apps/ospTestSuite/test_fixture.h index 6631d60e78..95ede78928 100644 --- a/apps/ospTestSuite/test_fixture.h +++ b/apps/ospTestSuite/test_fixture.h @@ -1,4 +1,4 @@ -// Copyright 2017-2019 Intel Corporation +// Copyright 2017-2020 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once @@ -75,57 +75,6 @@ class Base std::vector instances; }; -// Fixture class to test cornercases of intersection precision and epsilon -// handling; parametrized with renderer, sphere radius, distance factor, and -// whether the sphere is in origin -class SpherePrecision - : public Base, - public ::testing::TestWithParam> -{ - public: - SpherePrecision(); - void SetUp() override; - - protected: - float dist; - float radius; - bool move_cam; -}; - -// Test a texture colored by a volume. Creates a sphere colored by the torus -// volume It's parametrized with type of the renderer. -class TextureVolume : public Base, public ::testing::TestWithParam -{ - public: - TextureVolume(); - void SetUp() override; -}; - -// Test a texture colored by a volume. Creates a sphere colored by the torus -// volume It's parametrized with type of the renderer and background color -class DepthCompositeVolume - : public Base, - public ::testing::TestWithParam> -{ - public: - DepthCompositeVolume(); - void SetUp() override; - - private: - vec4f bgColor; -}; - -class RendererMaterialList : public Base, - public ::testing::TestWithParam -{ - public: - RendererMaterialList(); - void SetUp() override; -}; - // Fixture class used for tests that uses 'ospray_testing' scenes class FromOsprayTesting : public Base, diff --git a/apps/ospTestSuite/test_geometry.cpp b/apps/ospTestSuite/test_geometry.cpp index ebd3e9ba59..af01c8f521 100644 --- a/apps/ospTestSuite/test_geometry.cpp +++ b/apps/ospTestSuite/test_geometry.cpp @@ -1,29 +1,98 @@ -// Copyright 2017-2019 Intel Corporation +// Copyright 2017-2020 Intel Corporation // SPDX-License-Identifier: Apache-2.0 -#include "test_fixture.h" +#include "test_geometry.h" -using OSPRayTestScenes::FromOsprayTesting; -using OSPRayTestScenes::RendererMaterialList; -using OSPRayTestScenes::SpherePrecision; +namespace OSPRayTestScenes { -using namespace ospcommon; +SpherePrecision::SpherePrecision() +{ + auto params = GetParam(); + radius = std::get<0>(params); + dist = std::get<1>(params) * radius; + move_cam = std::get<2>(params); + rendererType = std::get<3>(params); +} -TEST_P(RendererMaterialList, material_list) +void SpherePrecision::SetUp() { - PerformRenderTest(); + Base::SetUp(); + + float fov = 160.0f * std::min(std::tan(radius / std::abs(dist)), 1.0f); + float cent = move_cam ? 0.0f : dist + radius; + + camera.setParam( + "position", vec3f(0.f, 0.f, move_cam ? -dist - radius : 0.0f)); + camera.setParam("direction", vec3f(0.f, 0.f, 1.f)); + camera.setParam("up", vec3f(0.f, 1.f, 0.f)); + camera.setParam("fovy", fov); + + renderer.setParam("pixelSamples", 16); + renderer.setParam("backgroundColor", vec4f(0.2f, 0.2f, 0.4f, 1.0f)); + if (rendererType == "scivis") { + renderer.setParam("aoSamples", 16); + renderer.setParam("aoIntensity", 1.f); + } else if (rendererType == "pathtracer") { + renderer.setParam("maxPathLength", 2); + } + + cpp::Geometry sphere("sphere"); + cpp::Geometry inst_sphere("sphere"); + + std::vector sph_center = {vec3f(-0.5f * radius, -0.3f * radius, cent), + vec3f(0.8f * radius, -0.3f * radius, cent), + vec3f(0.8f * radius, 1.5f * radius, cent), + vec3f(0.0f, -10001.3f * radius, cent)}; + + std::vector sph_radius = { + radius, 0.9f * radius, 0.9f * radius, 10000.f * radius}; + + sphere.setParam("sphere.position", cpp::Data(sph_center)); + sphere.setParam("sphere.radius", cpp::Data(sph_radius)); + sphere.commit(); + + inst_sphere.setParam("sphere.position", cpp::Data(vec3f(0.f))); + inst_sphere.setParam("sphere.radius", cpp::Data(90.f * radius)); + inst_sphere.commit(); + + cpp::GeometricModel model1(sphere); + cpp::GeometricModel model2(inst_sphere); + + cpp::Material sphereMaterial(rendererType, "obj"); + sphereMaterial.setParam("d", 1.0f); + sphereMaterial.commit(); + + model1.setParam("material", sphereMaterial); + model2.setParam("material", sphereMaterial); + + affine3f xfm(vec3f(0.01, 0, 0), + vec3f(0, 0.01, 0), + vec3f(0, 0, 0.01), + vec3f(-0.5f * radius, 1.6f * radius, cent)); + + AddModel(model1); + AddModel(model2, xfm); + + cpp::Light distant("distant"); + distant.setParam("intensity", 3.0f); + distant.setParam("direction", vec3f(0.3f, -4.0f, 0.8f)); + distant.setParam("color", vec3f(1.0f, 0.5f, 0.5f)); + distant.setParam("angularDiameter", 1.0f); + AddLight(distant); + + cpp::Light ambient = ospNewLight("ambient"); + ambient.setParam("intensity", 0.1f); + AddLight(ambient); } -INSTANTIATE_TEST_CASE_P(MaterialLists, - RendererMaterialList, - ::testing::Values("scivis", "pathtracer")); +// Test Instantiations ////////////////////////////////////////////////////// TEST_P(SpherePrecision, sphere) { PerformRenderTest(); } -INSTANTIATE_TEST_CASE_P(Intersection, +INSTANTIATE_TEST_SUITE_P(Intersection, SpherePrecision, ::testing::Combine(::testing::Values(0.001f, 3.e5f), ::testing::Values(3.f, 8000.0f, 2.e5f), @@ -35,7 +104,7 @@ TEST_P(FromOsprayTesting, test_scenes) PerformRenderTest(); } -INSTANTIATE_TEST_CASE_P(TestScenesGeometry, +INSTANTIATE_TEST_SUITE_P(TestScenesGeometry, FromOsprayTesting, ::testing::Combine(::testing::Values("cornell_box", "curves", @@ -43,14 +112,21 @@ INSTANTIATE_TEST_CASE_P(TestScenesGeometry, "empty", "random_spheres", "streamlines", - "subdivision_cube"), + "subdivision_cube", + "cornell_box_photometric", + "planes"), ::testing::Values("scivis", "pathtracer"))); -INSTANTIATE_TEST_CASE_P(TestScenesPtMaterials, +INSTANTIATE_TEST_SUITE_P(TestScenesClipping, FromOsprayTesting, - ::testing::Combine(::testing::Values("test_pt_glass", - "test_pt_luminous", - "test_pt_metal_roughness", - "test_pt_metallic_flakes", - "test_pt_obj"), - ::testing::Values("pathtracer"))); + ::testing::Combine(::testing::Values("clip_with_spheres", + "clip_with_boxes", + "clip_with_planes", + "clip_with_meshes", + "clip_with_subdivisions", + "clip_with_linear_curves", + "clip_with_bspline_curves", + "clip_gravity_spheres_volume", + "clip_perlin_noise_volumes"), + ::testing::Values("scivis", "pathtracer"))); +} // namespace OSPRayTestScenes diff --git a/apps/ospTestSuite/test_geometry.h b/apps/ospTestSuite/test_geometry.h new file mode 100644 index 0000000000..f1158c29d4 --- /dev/null +++ b/apps/ospTestSuite/test_geometry.h @@ -0,0 +1,28 @@ +// Copyright 2020 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#include "test_fixture.h" + +namespace OSPRayTestScenes { + +// Fixture class to test cornercases of intersection precision and epsilon +// handling; parametrized with renderer, sphere radius, distance factor, and +// whether the sphere is in origin +class SpherePrecision + : public Base, + public ::testing::TestWithParam> +{ + public: + SpherePrecision(); + void SetUp() override; + + protected: + float dist; + float radius; + bool move_cam; +}; + +} // namespace OSPRayTestScenes diff --git a/apps/ospTestSuite/test_light.cpp b/apps/ospTestSuite/test_light.cpp new file mode 100644 index 0000000000..c09631c74e --- /dev/null +++ b/apps/ospTestSuite/test_light.cpp @@ -0,0 +1,57 @@ +// Copyright 2020 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#include "test_light.h" + +namespace OSPRayTestScenes { + +SunSky::SunSky() +{ + rendererType = "pathtracer"; + samplesPerPixel = 1; +} + +void SunSky::SetUp() +{ + Base::SetUp(); + auto params = GetParam(); + + cpp::Light light("sunSky"); + float turb = std::get<2>(params); + light.setParam("up", std::get<0>(params)); + light.setParam("direction", std::get<1>(params)); + light.setParam("turbidity", turb); + light.setParam("albedo", std::get<3>(params)); + // lower brightness with high turbidity + light.setParam("intensity", 1.0f / turb); + AddLight(light); + + renderer.setParam("backgroundColor", vec4f(0.f, 0.f, 0.f, 1.0f)); +} + +// Test Instantiations ////////////////////////////////////////////////////// + +TEST_P(SunSky, parameter) +{ + PerformRenderTest(); +} + +INSTANTIATE_TEST_SUITE_P(Light, + SunSky, + ::testing::Combine(::testing::Values(vec3f(0.f, 0.8f, 0.4f)), + ::testing::Values(vec3f(0.f, 0.7f, -1.f), + vec3f(0.f, 0.4f, -1.f), + vec3f(0.f, 0.1f, -1.f), + vec3f(0.f, -0.3f, -1.f), + vec3f(0.f, -0.8f, 0.4f)), + ::testing::Values(1.0f, 3.0f, 10.0f), + ::testing::Values(0.0f))); + +INSTANTIATE_TEST_SUITE_P(Light2, + SunSky, + ::testing::Combine(::testing::Values(vec3f(0.2f, -0.5f, 0.f)), + ::testing::Values(vec3f(0.2f, 0.4f, -1.f), vec3f(0.f, 0.f, -1.f)), + ::testing::Values(2.0f), + ::testing::Values(0.0f, 1.0f))); + +} // namespace OSPRayTestScenes diff --git a/apps/ospTestSuite/test_light.h b/apps/ospTestSuite/test_light.h new file mode 100644 index 0000000000..28baaf9dc6 --- /dev/null +++ b/apps/ospTestSuite/test_light.h @@ -0,0 +1,19 @@ +// Copyright 2020 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#include "test_fixture.h" + +namespace OSPRayTestScenes { + +class SunSky : public Base, + public ::testing::TestWithParam> +{ + public: + SunSky(); + void SetUp() override; +}; + +} // namespace OSPRayTestScenes diff --git a/apps/ospTestSuite/test_volumetric.cpp b/apps/ospTestSuite/test_volumetric.cpp index 509a17e8c9..da5f1a356f 100644 --- a/apps/ospTestSuite/test_volumetric.cpp +++ b/apps/ospTestSuite/test_volumetric.cpp @@ -1,13 +1,169 @@ -// Copyright 2017-2019 Intel Corporation +// Copyright 2017-2020 Intel Corporation // SPDX-License-Identifier: Apache-2.0 -#include "test_fixture.h" +#include "test_volumetric.h" -using OSPRayTestScenes::DepthCompositeVolume; -using OSPRayTestScenes::FromOsprayTesting; -using OSPRayTestScenes::TextureVolume; +namespace OSPRayTestScenes { -INSTANTIATE_TEST_CASE_P(TestScenesVolumes, +// Helper functions ///////////////////////////////////////////////////////// + +// creates a torus +// volumetric data: stores data of torus +// returns created ospvolume of torus +static cpp::Volume CreateTorus(const int size) +{ + std::vector volumetricData(size * size * size, 0); + + const float r = 30; + const float R = 80; + const int size_2 = size / 2; + + for (int x = 0; x < size; ++x) { + for (int y = 0; y < size; ++y) { + for (int z = 0; z < size; ++z) { + const float X = x - size_2; + const float Y = y - size_2; + const float Z = z - size_2; + + const float d = (R - std::sqrt(X * X + Y * Y)); + volumetricData[size * size * x + size * y + z] = r * r - d * d - Z * Z; + } + } + } + + cpp::Volume torus("structuredRegular"); + torus.setParam("data", cpp::Data(vec3ul(size), volumetricData.data())); + torus.setParam("gridOrigin", vec3f(-0.5f, -0.5f, -0.5f)); + torus.setParam("gridSpacing", vec3f(1.f / size, 1.f / size, 1.f / size)); + return torus; +} + +///////////////////////////////////////////////////////////////////////////// + +TextureVolume::TextureVolume() +{ + rendererType = GetParam(); +} + +void TextureVolume::SetUp() +{ + Base::SetUp(); + + camera.setParam("position", vec3f(-0.7f, -1.4f, 0.f)); + camera.setParam("direction", vec3f(0.5f, 1.f, 0.f)); + camera.setParam("up", vec3f(0.f, 0.f, -1.f)); + + cpp::Volume torus = CreateTorus(256); + torus.commit(); + + cpp::VolumetricModel volumetricModel(torus); + + cpp::TransferFunction transferFun("piecewiseLinear"); + transferFun.setParam("valueRange", vec2f(-10000.f, 10000.f)); + + std::vector colors = { + vec3f(1.0f, 0.0f, 0.0f), vec3f(0.0f, 1.0f, 0.0f)}; + + std::vector opacities = {1.0f, 1.0f}; + + transferFun.setParam("color", cpp::Data(colors)); + transferFun.setParam("opacity", cpp::Data(opacities)); + transferFun.commit(); + + volumetricModel.setParam("transferFunction", transferFun); + volumetricModel.commit(); + + cpp::Texture tex("volume"); + tex.setParam("volume", volumetricModel); + tex.commit(); + + cpp::Material sphereMaterial(rendererType, "obj"); + sphereMaterial.setParam("map_kd", tex); + sphereMaterial.commit(); + + cpp::Geometry sphere("sphere"); + sphere.setParam("sphere.position", cpp::Data(vec3f(0.f))); + sphere.setParam("radius", 0.51f); + sphere.commit(); + + cpp::GeometricModel model(sphere); + model.setParam("material", sphereMaterial); + AddModel(model); + + cpp::Light ambient("ambient"); + ambient.setParam("intensity", 0.5f); + AddLight(ambient); +} + +DepthCompositeVolume::DepthCompositeVolume() +{ + auto params = GetParam(); + rendererType = std::get<0>(params); + bgColor = std::get<1>(params); +} + +void DepthCompositeVolume::SetUp() +{ + Base::SetUp(); + + camera.setParam("position", vec3f(0.f, 0.f, 1.f)); + camera.setParam("direction", vec3f(0.f, 0.f, -1.f)); + camera.setParam("up", vec3f(0.f, 1.f, 0.f)); + + cpp::Volume torus = CreateTorus(256); + torus.commit(); + + cpp::VolumetricModel volumetricModel(torus); + + cpp::TransferFunction transferFun("piecewiseLinear"); + transferFun.setParam("valueRange", vec2f(-10000.f, 10000.f)); + + std::vector colors = { + vec3f(1.0f, 0.0f, 0.0f), vec3f(0.0f, 1.0f, 0.0f)}; + + std::vector opacities = {0.05f, 1.0f}; + + transferFun.setParam("color", cpp::Data(colors)); + transferFun.setParam("opacity", cpp::Data(opacities)); + transferFun.commit(); + + volumetricModel.setParam("transferFunction", transferFun); + volumetricModel.commit(); + + AddModel(volumetricModel); + + cpp::Texture depthTex("texture2d"); + + std::vector texData(imgSize.product()); + for (int y = 0; y < imgSize.y; y++) { + for (int x = 0; x < imgSize.x; x++) { + const size_t index = imgSize.x * y + x; + if (x < imgSize.x / 3) { + texData[index] = 999.f; + } else if (x < (imgSize.x * 2) / 3) { + texData[index] = 0.75f; + } else { + texData[index] = 0.00001f; + } + } + } + + depthTex.setParam("format", OSP_TEXTURE_R32F); + depthTex.setParam("filter", OSP_TEXTURE_FILTER_NEAREST); + depthTex.setParam("data", cpp::Data(imgSize, texData.data())); + depthTex.commit(); + + renderer.setParam("map_maxDepth", depthTex); + renderer.setParam("backgroundColor", bgColor); + + cpp::Light ambient("ambient"); + ambient.setParam("intensity", 0.5f); + AddLight(ambient); +} + +// Test Instantiations ////////////////////////////////////////////////////// + +INSTANTIATE_TEST_SUITE_P(TestScenesVolumes, FromOsprayTesting, ::testing::Combine(::testing::Values("gravity_spheres_volume", "perlin_noise_volumes", @@ -19,17 +175,19 @@ TEST_P(TextureVolume, simple) PerformRenderTest(); } -INSTANTIATE_TEST_CASE_P(Renderers, TextureVolume, ::testing::Values("scivis")); +INSTANTIATE_TEST_SUITE_P(Renderers, TextureVolume, ::testing::Values("scivis")); TEST_P(DepthCompositeVolume, simple) { PerformRenderTest(); } -INSTANTIATE_TEST_CASE_P(Renderers, +INSTANTIATE_TEST_SUITE_P(Renderers, DepthCompositeVolume, ::testing::Combine(::testing::Values("scivis"), ::testing::Values(vec4f(0.f), vec4f(1.f), vec4f(0.f, 0.f, 0.f, 1.f), vec4f(1.f, 0.f, 0.f, 0.5f)))); + +} // namespace OSPRayTestScenes diff --git a/apps/ospTestSuite/test_volumetric.h b/apps/ospTestSuite/test_volumetric.h new file mode 100644 index 0000000000..0c4b18888f --- /dev/null +++ b/apps/ospTestSuite/test_volumetric.h @@ -0,0 +1,31 @@ +// Copyright 2020 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#include "test_fixture.h" + +namespace OSPRayTestScenes { + +// Test a texture colored by a volume. Creates a sphere colored by the torus +// volume It's parametrized with type of the renderer. +class TextureVolume : public Base, public ::testing::TestWithParam +{ + public: + TextureVolume(); + void SetUp() override; +}; + +// Test a texture colored by a volume. Creates a sphere colored by the torus +// volume It's parametrized with type of the renderer and background color +class DepthCompositeVolume + : public Base, + public ::testing::TestWithParam> +{ + public: + DepthCompositeVolume(); + void SetUp() override; + + private: + vec4f bgColor; +}; + +} // namespace OSPRayTestScenes diff --git a/apps/ospTutorial/CMakeLists.txt b/apps/ospTutorial/CMakeLists.txt index 128dd5f0e6..c4bc9770c8 100644 --- a/apps/ospTutorial/CMakeLists.txt +++ b/apps/ospTutorial/CMakeLists.txt @@ -1,4 +1,4 @@ -## Copyright 2018-2019 Intel Corporation +## Copyright 2018-2020 Intel Corporation ## SPDX-License-Identifier: Apache-2.0 if (NOT OSPRAY_APPS_EXAMPLES) @@ -6,15 +6,15 @@ if (NOT OSPRAY_APPS_EXAMPLES) endif() # build ospTutorial, for demonstration and API testing -add_executable(ospTutorial ospTutorial.c) +add_executable(ospTutorial ${OSPRAY_RESOURCE} ospTutorial.c) target_link_libraries(ospTutorial PRIVATE ospray) # C++ version -add_executable(ospTutorialCpp ospTutorial.cpp) +add_executable(ospTutorialCpp ${OSPRAY_RESOURCE} ospTutorial.cpp) target_link_libraries(ospTutorialCpp PRIVATE ospray) # async version -add_executable(ospTutorialAsync ospTutorialAsync.c) +add_executable(ospTutorialAsync ${OSPRAY_RESOURCE} ospTutorialAsync.c) target_link_libraries(ospTutorialAsync PRIVATE ospray) install(TARGETS ospTutorial ospTutorialCpp ospTutorialAsync diff --git a/apps/ospTutorial/ospTutorialAsync.c b/apps/ospTutorial/ospTutorialAsync.c index 262c4ae00f..c851f51b98 100644 --- a/apps/ospTutorial/ospTutorialAsync.c +++ b/apps/ospTutorial/ospTutorialAsync.c @@ -1,4 +1,4 @@ -// Copyright 2009-2019 Intel Corporation +// Copyright 2009-2020 Intel Corporation // SPDX-License-Identifier: Apache-2.0 /* This is a small example tutorial of how to use OSPRay's async API in an @@ -115,6 +115,7 @@ int main(int argc, const char **argv) // render 10 more frames, which are accumulated to result in a better // converged image + printf("starting accumulation\n"); for (int frames = 0; frames < 10; frames++) { for (int i = 0; i < 2; ++i) { futures[i] = @@ -122,9 +123,17 @@ int main(int argc, const char **argv) } for (int i = 0; i < 2; ++i) { ospWait(futures[i], OSP_FRAME_FINISHED); - ospRelease(futures[i]); + if (frames < 9) // don't release future of last frame yet + ospRelease(futures[i]); } } + for (int i = 1; i >= 0; --i) { + printf("...done!\n"); + printf("variance of render %i is now %f\n", + i, + ospGetVariance(framebuffers[i])); + ospRelease(futures[i]); + } fb = (uint32_t *)ospMapFrameBuffer(framebuffers[0], OSP_FB_COLOR); writePPM("accumulatedFrame-scene1.ppm", &imgSizes[0], fb); @@ -271,7 +280,7 @@ void buildScene1(OSPCamera *camera, *framebuffer = ospNewFrameBuffer(imgSize.x, imgSize.y, OSP_FB_SRGBA, - OSP_FB_COLOR | /*OSP_FB_DEPTH |*/ OSP_FB_ACCUM); + OSP_FB_COLOR | /*OSP_FB_DEPTH |*/ OSP_FB_ACCUM | OSP_FB_VARIANCE); } void buildScene2(OSPCamera *camera, @@ -396,5 +405,5 @@ void buildScene2(OSPCamera *camera, *framebuffer = ospNewFrameBuffer(imgSize.x, imgSize.y, OSP_FB_SRGBA, - OSP_FB_COLOR | /*OSP_FB_DEPTH |*/ OSP_FB_ACCUM); + OSP_FB_COLOR | /*OSP_FB_DEPTH |*/ OSP_FB_ACCUM | OSP_FB_VARIANCE); } diff --git a/apps/ospTutorial/ospTutorialFindospray/CMakeLists.txt b/apps/ospTutorial/ospTutorialFindospray/CMakeLists.txt index ab8e653893..364c21312d 100644 --- a/apps/ospTutorial/ospTutorialFindospray/CMakeLists.txt +++ b/apps/ospTutorial/ospTutorialFindospray/CMakeLists.txt @@ -1,18 +1,5 @@ -## ======================================================================== ## -## Copyright 2019 Intel Corporation ## -## ## -## Licensed under the Apache License, Version 2.0 (the "License"); ## -## you may not use this file except in compliance with the License. ## -## You may obtain a copy of the License at ## -## ## -## http://www.apache.org/licenses/LICENSE-2.0 ## -## ## -## Unless required by applicable law or agreed to in writing, software ## -## distributed under the License is distributed on an "AS IS" BASIS, ## -## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## -## See the License for the specific language governing permissions and ## -## limitations under the License. ## -## ======================================================================== ## +## Copyright 2019 Intel Corporation +## SPDX-License-Identifier: Apache-2.0 # NOTE: This CMakeLists.txt is intended to be used to exercise an OSPRay # install and demonstrate how external applications can build against diff --git a/apps/utility/CMakeLists.txt b/apps/utility/CMakeLists.txt index 10d9bf179e..769d30303f 100644 --- a/apps/utility/CMakeLists.txt +++ b/apps/utility/CMakeLists.txt @@ -1,12 +1,12 @@ -## Copyright 2009-2019 Intel Corporation +## Copyright 2009-2020 Intel Corporation ## SPDX-License-Identifier: Apache-2.0 if (NOT OSPRAY_APPS_TESTING) return() endif() -add_executable(ospConvertRawToAMR ospConvertRawToAMR.cpp) -target_link_libraries(ospConvertRawToAMR PRIVATE raw_to_amr) +add_executable(ospConvertRawToAMR ${OSPRAY_RESOURCE} ospConvertRawToAMR.cpp) +target_link_libraries(ospConvertRawToAMR PRIVATE raw_to_amr ospray) install(TARGETS ospConvertRawToAMR DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT apps diff --git a/cmake/ospray_macros.cmake b/cmake/ospray_macros.cmake index 637820a3c6..8fb45929b8 100644 --- a/cmake/ospray_macros.cmake +++ b/cmake/ospray_macros.cmake @@ -1,4 +1,4 @@ -## Copyright 2009-2019 Intel Corporation +## Copyright 2009-2020 Intel Corporation ## SPDX-License-Identifier: Apache-2.0 include(CMakeFindDependencyMacro) @@ -141,17 +141,6 @@ macro(ospray_configure_ispc_isa) message(STATUS "OSPRay AVX512SKX ISA target enabled.") endif() - # NOTE(jda) - These checks are due to temporary Open VKL limitations with - # iterators, they ought to be removed when these limitations are - # lifted in a future version. - if(NOT EMBREE_ISA_SUPPORTS_AVX512SKX AND OPENVKL_ISA_AVX512SKX) - message(FATAL_ERROR "Currently Open VKL cannot have AVX512SKX compiled in without Embree AVX512SKX support") - endif() - - if(NOT EMBREE_ISA_SUPPORTS_AVX512KNL AND OPENVKL_ISA_AVX512KNL) - message(FATAL_ERROR "Currently Open VKL cannot have AVX512KNL compiled in without Embree AVX512KNL support") - endif() - elseif (OSPRAY_BUILD_ISA STREQUAL "AVX512SKX") if(NOT EMBREE_ISA_SUPPORTS_AVX512SKX) @@ -441,8 +430,10 @@ macro(ospray_find_openvkl OPENVKL_VERSION_REQUIRED) else() get_target_property(OPENVKL_INCLUDE_DIRS openvkl::openvkl INTERFACE_INCLUDE_DIRECTORIES) + get_target_property(CONFIGURATIONS openvkl::openvkl IMPORTED_CONFIGURATIONS) + list(GET CONFIGURATIONS 0 CONFIGURATION) get_target_property(OPENVKL_LIBRARY openvkl::openvkl - IMPORTED_LOCATION_RELEASE) + IMPORTED_LOCATION_${CONFIGURATION}) message(STATUS "Found Open VKL: ${OPENVKL_LIBRARY}") endif() endmacro() diff --git a/cmake/ospray_options.cmake b/cmake/ospray_options.cmake index 067c45b0fc..0146b8ddfa 100644 --- a/cmake/ospray_options.cmake +++ b/cmake/ospray_options.cmake @@ -10,9 +10,9 @@ include(GNUInstallDirs) set(OSPRAY_CMAKECONFIG_DIR "${CMAKE_INSTALL_LIBDIR}/cmake/ospray-${OSPRAY_VERSION}") -set(OSPCOMMON_VERSION_REQUIRED 1.1.0) +set(OSPCOMMON_VERSION_REQUIRED 1.3.0) set(EMBREE_VERSION_REQUIRED 3.8.0) -set(OPENVKL_VERSION_REQUIRED 0.8.0) +set(OPENVKL_VERSION_REQUIRED 0.9.0) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) diff --git a/cmake/ospray_version.cmake b/cmake/ospray_version.cmake index 5459ed08a4..0a82dbb4a3 100644 --- a/cmake/ospray_version.cmake +++ b/cmake/ospray_version.cmake @@ -1,9 +1,9 @@ -## Copyright 2009-2019 Intel Corporation +## Copyright 2009-2020 Intel Corporation ## SPDX-License-Identifier: Apache-2.0 set(OSPRAY_VERSION_MAJOR 2) -set(OSPRAY_VERSION_MINOR 0) -set(OSPRAY_VERSION_PATCH 1) +set(OSPRAY_VERSION_MINOR 1) +set(OSPRAY_VERSION_PATCH 0) set(OSPRAY_SOVERSION 2) set(OSPRAY_VERSION_GITHASH 0) set(OSPRAY_VERSION_NOTE "") diff --git a/cmake/package.cmake b/cmake/package.cmake index 33c88b66a4..966eab17df 100644 --- a/cmake/package.cmake +++ b/cmake/package.cmake @@ -1,4 +1,4 @@ -## Copyright 2009-2019 Intel Corporation +## Copyright 2009-2020 Intel Corporation ## SPDX-License-Identifier: Apache-2.0 set(CMAKE_INSTALL_SCRIPTDIR scripts) @@ -44,9 +44,17 @@ install(DIRECTORY ${PROJECT_SOURCE_DIR}/ospray/include/ospray # install documentation ############################################################## -install(FILES ${PROJECT_SOURCE_DIR}/LICENSE.txt DESTINATION ${CMAKE_INSTALL_DOCDIR} COMPONENT lib) -install(FILES ${PROJECT_SOURCE_DIR}/CHANGELOG.md DESTINATION ${CMAKE_INSTALL_DOCDIR} COMPONENT lib) -install(FILES ${PROJECT_SOURCE_DIR}/README.md DESTINATION ${CMAKE_INSTALL_DOCDIR} COMPONENT lib) +install(FILES + ${PROJECT_SOURCE_DIR}/LICENSE.txt + ${PROJECT_SOURCE_DIR}/third-party-programs.txt + ${PROJECT_SOURCE_DIR}/third-party-programs-TBB.txt + ${PROJECT_SOURCE_DIR}/third-party-programs-Embree.txt + ${PROJECT_SOURCE_DIR}/third-party-programs-OpenVKL.txt + ${PROJECT_SOURCE_DIR}/third-party-programs-OIDN.txt + ${PROJECT_SOURCE_DIR}/third-party-programs-DNNL.txt + ${PROJECT_SOURCE_DIR}/CHANGELOG.md + ${PROJECT_SOURCE_DIR}/README.md + DESTINATION ${CMAKE_INSTALL_DOCDIR} COMPONENT lib) install(FILES ${PROJECT_SOURCE_DIR}/readme.pdf DESTINATION ${CMAKE_INSTALL_DOCDIR} COMPONENT lib OPTIONAL) ############################################################## diff --git a/doc/Makefile b/doc/Makefile index ad8342a56b..9ec378ef8a 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -4,17 +4,15 @@ webpages := $(addprefix www/, $(addsuffix .html, index examples documentation ga process_version := $(addprefix tmp/, $(addsuffix .md, examples getting_ospray readme_head)) tmptexfiles := $(addprefix tmp/, $(addsuffix .tex, overview changelog compilation api examples)) 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)) -images_png := $(addprefix images/, $(addsuffix .png, diffuse_rooms normalmap_frustum tutorial_accumulatedframe tutorial_firstframe ospExamples)) -images_fig := spot_light quad_light hdri_light +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_svg := gitter_badge structured_spherical_coords images_webonly := $(addprefix images/, VESTEC-Logo-web.png $(addsuffix .jpg, stingray $(addprefix teaser_, clouds cosmos hdospray materials moana rm tapestry))) -scripts := $(addprefix scripts/, $(addsuffix .min.js, bootstrap jquery popper)) images_fig2pdf := $(addprefix tmp/, $(addsuffix .pdf, $(images_fig))) images_svg2pdf := $(addprefix tmp/, $(addsuffix .pdf, $(images_svg))) images_fig2png := $(addprefix images/, $(addsuffix .png, $(images_fig))) webimages := $(addprefix www/, $(images_webonly) $(images_jpg) $(images_png) $(images_fig2png) $(addprefix images/, $(addsuffix .svg, $(images_svg)))) -webscripts := $(addprefix www/, $(scripts)) pdfimages := $(images_jpg) $(images_png) $(images_fig2pdf) $(images_fig2svg) PANDOC := pandoc @@ -27,7 +25,7 @@ ifneq "$(PDOK)" "1" endif all: www doc pdf -www: $(webpages) www/stylesheet.css $(webimages) $(webscripts) +www: $(webpages) www/stylesheet.css $(webimages) | wwwscripts doc: ../README.md pdf: ../readme.pdf @@ -51,7 +49,7 @@ tmp/%.md: %.md tmp/version tmp/links_local.md tmp/images_web.md $(replace_version) $(PANDOC) $@ tmp/links_local.md tmp/images_web.md -o $@ -tmp/links.md: links.md +tmp/links.md: links.md $(wildcard ../.git/HEAD) $(if_is_devel_then) \ sed -e "s/OSPRay_readme.pdf/OSPRay_readme_devel.pdf/g" $< > $@;\ else cp $< $@ ;\ @@ -80,9 +78,10 @@ $(webimages): | wwwimgdir wwwimgdir: @mkdir -p www/images -$(webscripts): | wwwscriptdir -wwwscriptdir: - @mkdir -p www/scripts +wwwscripts: | wwwdir + if [[ -d ospray-doc/scripts ]] ; then \ + cp -r ospray-doc/scripts/ www ;\ + fi $(process_version) tmp/version ../readme.pdf $(tmptexfiles) tmp/api.md tmp/api_web.md tmp/changelog_web.md tmp/compilation.md tmp/compilation_web.md tmp/links.md tmp/links_local.md tmp/images_web.md tmp/images_local_pdf.md tmp/images_local_png.md: | tmpdir tmpdir: @@ -153,12 +152,6 @@ images/%: fi \ fi -### scripts -######################################################################## - -www/scripts/%: scripts/% - cp $< $@ - ### markdown ######################################################################## @@ -188,6 +181,9 @@ tmp/api.tex: filter-latex.py api.md tmp/links_local.md tmp/images_local_pdf.md tmp/compilation.tex: filter-latex.py compilation.md tmp/links_local.md tmp/images_local_pdf.md $(markdown2tex) --indented-code-classes=sh +tmp/changelog.tex: filter-sectionnumbers.py changelog.md + $(markdown2tex) + changelog.md: ../CHANGELOG.md sed -e "s@README.md@@" $< > $@ diff --git a/doc/api.md b/doc/api.md index 9413088a23..d62d1f9909 100644 --- a/doc/api.md +++ b/doc/api.md @@ -156,7 +156,7 @@ to avoid leaking the underlying device object. OSPRay allows applications to query runtime properties of a device in order to do enhanced validation of what device was loaded at runtime. The following function can be used to get these device-specific -properties (attiributes about the device, not paramter values) +properties (attributes about the device, not parameter values) int64_t ospDeviceGetProperty(OSPDevice, OSPDeviceProperty); @@ -376,13 +376,13 @@ Thus the most efficient way to specify a data array from the application is to created a shared data array, which is done with OSPData ospNewSharedData(const void *sharedData, - OSPDataType, - uint64_t numItems1, - int64_t byteStride1 = 0, - uint64_t numItems2 = 1, - int64_t byteStride2 = 0, - uint64_t numItems3 = 1, - int64_t byteStride3 = 0); + OSPDataType, + uint64_t numItems1, + int64_t byteStride1 = 0, + uint64_t numItems2 = 1, + int64_t byteStride2 = 0, + uint64_t numItems3 = 1, + int64_t byteStride3 = 0); The call returns an `OSPData` handle to the created array. The calling program guarantees that the `sharedData` pointer will remain valid for @@ -395,8 +395,8 @@ and can also be negative. If `byteStride` is zero it will be determined automatically (e.g., as `sizeof(type)`). Strides do not need to be ordered, i.e., `byteStride2` can be smaller than `byteStride1`, which is equivalent to a transpose. However, if the stride should be calculated, -then an ordering like `byteStride1 < byteStride2` is assumed to -disambiguate. +then an ordering in dimensions is assumed to disambiguate, i.e., +`byteStride1 < byteStride2 < byteStride3`. The enum type `OSPDataType` describes the different element types that can be represented in OSPRay; valid constants are listed in the table @@ -423,6 +423,7 @@ below. OSP_UCHAR 8\ bit unsigned character scalar OSP_VEC[234]UC ... and [234]-element vector OSP_USHORT 16\ bit unsigned integer scalar + OSP_VEC[234]US ... and [234]-element vector OSP_INT 32\ bit signed integer scalar OSP_VEC[234]I ... and [234]-element vector OSP_UINT 32\ bit unsigned integer scalar @@ -436,26 +437,29 @@ below. OSP_DOUBLE 64\ bit double precision floating-point scalar OSP_BOX[1234]I 32\ bit integer box (lower + upper bounds) OSP_BOX[1234]F 32\ bit single precision floating-point box (lower + upper bounds) - OSP_LINEAR[234]F 32\ bit single precision floating-point linear transform - OSP_AFFINE[234]F 32\ bit single precision floating-point affine transform + OSP_LINEAR[23]F 32\ bit single precision floating-point linear transform ([23] vectors) + OSP_AFFINE[23]F 32\ bit single precision floating-point affine transform (linear transform plus translation) OSP_VOID_PTR raw memory address (only found in module extensions) ---------------------- ----------------------------------------------- : Valid named constants for `OSPDataType`. +If the elements of the array are handles to objects, then their +reference counter is incremented. + An opaque `OSPData` with memory allocated by OSPRay is created with OSPData ospNewData(OSPDataType, - uint32_t numItems1, - uint32_t numItems2 = 1, - uint32_t numItems3 = 1); + uint32_t numItems1, + uint32_t numItems2 = 1, + uint32_t numItems3 = 1); To allow for (partial) copies or updates of data arrays use void ospCopyData(const OSPData source, - OSPData destination, - uint32_t destinationIndex1 = 0, - uint32_t destinationIndex2 = 0, - uint32_t destinationIndex3 = 0); + OSPData destination, + uint32_t destinationIndex1 = 0, + uint32_t destinationIndex2 = 0, + uint32_t destinationIndex3 = 0); which will copy the whole^[The number of items to be copied is defined by the size of the source array] content of the `source` array into @@ -471,7 +475,7 @@ shared with OSPData by the application (created with - the source array must be shared as well (thus `ospCopyData` cannot be used to read opaque data) - - if source and destination memory overlaps (aliasing), then behaviour + - if source and destination memory overlaps (aliasing), then behavior is undefined - except if source and destination regions are identical (including matching strides), which can be used by application to mark that @@ -677,7 +681,7 @@ the vertices and data values. Vertex ordering is the same as vertex. To maintain VTK data compatibility an index array may be specified via -the `indexPrefixed` array that allow vertex indices to be interleaved +the `indexPrefixed` array that allows vertex indices to be interleaved with cell sizes in the following format: $n, id_1, ..., id_n, m, id_1, ..., id_m$. @@ -721,7 +725,7 @@ with cell sizes in the following format: $n, id_1, ..., id_n, m, id_1, inaccuracies may appear if hex is not parallelepiped - bool precomputedNormals true whether to accelerate by precomputing, + bool precomputedNormals false whether to accelerate by precomputing, at a cost of 12 bytes/face ------------------- ------------------ -------- --------------------------------------- : Additional configuration parameters for unstructured volumes. @@ -750,6 +754,7 @@ to `ospNewTransferFunction` and it is controlled by these parameters: ------------ ----------- ---------------------------------------------- : Parameters accepted by the linear transfer function. +The arrays `color` and `opacity` can be of different length. ### VolumetricModels @@ -789,7 +794,7 @@ maximum of 2^32^ primitives. ### Mesh -A mesh consiting of either triangles or quads is created by calling +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: @@ -819,47 +824,48 @@ A mesh consisting of subdivision surfaces, created by specifying a geometry of type "`subdivision`". Once created, a subdivision recognizes the following parameters: - --------------- -------------------- --------------------------------- ------------------------------------------------- - Type Name Default Description - --------------- -------------------- --------------------------------- ------------------------------------------------- - vec3f[] vertex.position NULL [data] array of vertex positions - - vec4f[] vertex.color NULL [data] array of vertex colors (RGBA) + ------- ------------------- -------------------------------------------------- + Type Name Description + ------- ------------------- -------------------------------------------------- + vec3f[] vertex.position [data] array of vertex positions - vec2f[] vertex.texcoord NULL [data] array of vertex texture coordinates + vec4f[] vertex.color optional [data] array of vertex colors (RGBA) - float level 5 global level of tessellation, default is 5 + vec2f[] vertex.texcoord optional [data] array of vertex texture + coordinates - uint[] index NULL [data] array of indices (into the vertex array(s)) + float level global level of tessellation, default 5 - float[] index.level NULL [data] array of per-edge levels of tessellation, - overrides global level + uint[] index [data] array of indices (into the vertex array(s)) - uint[] face NULL [data] array holding the number of indices/edges - (3 to 15) per face + float[] index.level optional [data] array of per-edge levels of + tessellation, overrides global level - vec2i[] edgeCrease.index NULL [data] array of edge crease indices + uint[] face optional [data] array holding the number of + indices/edges (3 to 15) per face, + defaults to 4 (a pure quad mesh) - float[] edgeCrease.weight NULL [data] array of edge crease weights + vec2i[] edgeCrease.index optional [data] array of edge crease indices - uint[] vertexCrease.index NULL [data] array of vertex crease indices + float[] edgeCrease.weight optional [data] array of edge crease weights - float[] vertexCrease.weight NULL [data] array of vertex crease weights + uint[] vertexCrease.index optional [data] array of vertex crease indices - int mode `OSP_SUBDIVISION_SMOOTH_BOUNDARY` subdivision edge boundary mode. - Supported modes are: + float[] vertexCrease.weight optional [data] array of vertex crease weights - `OSP_SUBDIVISION_NO_BOUNDARY` + int mode subdivision edge boundary mode, supported modes + are: - `OSP_SUBDIVISION_SMOOTH_BOUNDARY` + `OSP_SUBDIVISION_NO_BOUNDARY` - `OSP_SUBDIVISION_PIN_CORNERS` + `OSP_SUBDIVISION_SMOOTH_BOUNDARY` (default) - `OSP_SUBDIVISION_PIN_BOUNDARY` + `OSP_SUBDIVISION_PIN_CORNERS` - `OSP_SUBDIVISION_PIN_ALL` + `OSP_SUBDIVISION_PIN_BOUNDARY` - --------------- -------------------- --------------------------------- ------------------------------------------------- + `OSP_SUBDIVISION_PIN_ALL` + ------- ------------------- -------------------------------------------------- : Parameters defining a Subdivision geometry. The `vertex` and `index` arrays are mandatory to create a valid @@ -960,7 +966,7 @@ discussion of curve types and data formatting). If a constant `radius` is used and positions are specified in a `vec3f[]` type of `vertex.position` format, then type/basis defaults to `OSP_ROUND` and `OSP_LINEAR` (this is the fastest and most memory -efficient mode). Implementation is with round linear segements where +efficient mode). Implementation is with round linear segments where each segment corresponds to a link between two vertices. The following section describes the properties of different curve basis' @@ -984,7 +990,7 @@ OSP_BSPLINE vertex buffer. This basis is not interpolating, thus the curve does in general not go through any of the control points directly. Using this basis, 3 control points can be shared for two continuous neighboring -curve segments, e.g. the curves $(p0, p1, p2, p3)$ and $(p1, p2, p3, +curve segments, e.g., the curves $(p0, p1, p2, p3)$ and $(p1, p2, p3, p4)$ are C1 continuous. This feature make this basis a good choice to construct continuous multi-segment curves, as memory consumption can be kept minimal. @@ -1036,6 +1042,21 @@ geometry by calling `ospNewGeometry` with type string "`box`". ---------- ---------- ------------------------------------------------------ : Parameters defining a boxes geometry. +### Planes + +OSPRay can directly render planes defined by plane equation coefficients +in its implicit form $ax + by + cz + d = 0$. By default planes are +infinite but their extents can be limited by defining optional bounding +boxes. A planes geometry can be created by calling `ospNewGeometry` with +type string "`plane`". + + Type Name Description + ---------- ------------------ ------------------------------------------------- + vec4f[] plane.coefficients [data] array of plane coefficients $(a, b, c, d)$ + box3f[] plane.bounds optional [data] array of bounding boxes + ---------- ------------------ ------------------------------------------------- + : Parameters defining a planes geometry. + ### Isosurfaces OSPRay can directly render multiple isosurfaces of a volume without @@ -1048,7 +1069,7 @@ to the `volume`. ------------------ --------- -------------------------------------------------- float isovalue single isovalues float[] isovalue [data] array of isovalues - OSPVolumetricModel volume handle of the [VolumetricModels] to be isosurfaced + OSPVolumetricModel volume handle of the [VolumetricModel] to be isosurfaced ------------------ --------- -------------------------------------------------- : Parameters defining an isosurfaces geometry. @@ -1063,30 +1084,38 @@ and material information. To create a geometric model, call Color and material are fetched with the primitive ID of the hit (clamped to the valid range, thus a single color or material is fine), or mapped -first via the `index` array (if present). All paramters are optional, +first via the `index` array (if present). All parameters are optional, however, some renderers (notably the [path tracer]) require a material to be set. Materials are either handles of `OSPMaterial`, or indices into the `material` array on the [renderer], which allows to build a [world] which can be used by different types of renderers. - ------------------------ --------- ---------------------------------------------------- - Type Name Description - ------------------------ --------- ---------------------------------------------------- - OSPMaterial / uint32 material optional [material] applied to the geometry, may be - an index into the `material` parameter on the - [renderer] (if it exists) +An `invertNormals` flag allows to invert (shading) normal vectors of the +rendered geometry. That is particularly useful for clipping. By changing +normal vectors orientation one can control whether inside or outside of +the clipping geometry is being removed. For example, a clipping geometry +with normals oriented outside clips everything what's inside. + + ------------------------ -------------- ---------------------------------------------------- + Type Name Description + ------------------------ -------------- ---------------------------------------------------- + OSPMaterial / uint32 material optional [material] applied to the geometry, may be + an index into the `material` parameter on the + [renderer] (if it exists) - vec4f color optional color assigned to the geometry + vec4f color optional color assigned to the geometry - OSPMaterial[] / uint32[] material optional [data] array of (per-primitive) materials, - may be an index into the `material` parameter on - the renderer (if it exists) + OSPMaterial[] / uint32[] material optional [data] array of (per-primitive) materials, + may be an index into the `material` parameter on + the renderer (if it exists) - vec4f[] color optional [data] array of (per-primitive) colors + vec4f[] color optional [data] array of (per-primitive) colors - uint8[] index optional [data] array of per-primitive indices into - `color` and `material` - ------------------------ --------- ---------------------------------------------------- + uint8[] index optional [data] array of per-primitive indices into + `color` and `material` + + bool invertNormals inverts all shading normals (Ns), default false + ------------------------ -------------- ---------------------------------------------------- : Parameters understood by GeometricModel. @@ -1149,31 +1178,49 @@ 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 +### Spotlight / Photometric Light The spotlight is a light emitting into a cone of directions. It is created by passing the type string "`spot`" to `ospNewLight`. 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, in world-space - - 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` - ------ ------------- ----------- ---------------------------------------------- + ---------- --------------------- ----------- --------------------------------- + Type Name Default Description + ---------- --------------------- ----------- --------------------------------- + vec3f position $(0, 0, 0)$ the center of the spotlight, in + world-space + + 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[] 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. ![Angles used by the spotlight.][imgSpotLight] @@ -1182,6 +1229,21 @@ Setting the radius to a value greater than zero will result in soft shadows when the renderer uses stochastic sampling (like the [path tracer]). +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`. + +![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 @@ -1241,6 +1303,28 @@ and `intensity`](#lights)). It is created by passing the type string Note that the [SciVis renderer] uses ambient lights to control the color and intensity of the computed ambient occlusion (AO). +### Sun-Sky Light + +The sun-sky light is a combination of a `distant` light for the sun and +a procedural `hdri` light for the sky. It is created by passing the type +string "`sunSky`" to `ospNewLight`. The sun-sky light surrounds the +scene and illuminates it from infinity and can be used for rendering +outdoor scenes. The radiance values are calculated using the +Hošek-Wilkie sky model and solar radiance function. In addition to the +[general parameters](#lights) the following special parameters are +supported: + + Type Name Default Description + --------- ---------- ----------- -------------------------------------------- + vec3f up $(0, 1, 0)$ zenith of sky in world-space + vec3f direction $(0, -1, 0)$ main emission direction of the sun + float turbidity 3 atmospheric turbidity due to particles, in [1–10] + float albedo 0.3 ground reflectance, in [0–1] + --------- ---------- ----------- -------------------------------------------- + : Special parameters accepted by the `sunSky` light. + +The lowest elevation for the sun is restricted to the horizon. + ### Emissive Objects The [path tracer] will consider illumination by [geometries] which have @@ -1259,31 +1343,48 @@ create a group call OSPGroup ospNewGroup(); -Groups take arrays of geometric models and volumetric models, but they -are optional. In other words, there is no need to create empty arrays if -there are no geometries or volumes in the group. - - -------------------- --------------- ---------- -------------------------------------- - Type Name Default Description - -------------------- --------------- ---------- -------------------------------------- - OSPGeometricModel[] geometry NULL [data] array of [GeometricModels] - - OSPVolumetricModel[] volume NULL [data] array of [VolumetricModels] - - bool dynamicScene false use RTC_SCENE_DYNAMIC flag (faster - BVH build, slower ray traversal), - otherwise uses RTC_SCENE_STATIC flag - (faster ray traversal, slightly - slower BVH build) - - bool compactMode false tell Embree to use a more compact BVH - in memory by trading ray traversal - performance - - bool robustMode false tell Embree to enable more robust ray - intersection code paths (slightly - slower) - -------------------- --------------- ---------- --------------------------------------- +Groups take arrays of geometric models, volumetric models and clipping +geometric models, but they are optional. In other words, there is no need +to create empty arrays if there are no geometries or volumes in the group. + +By adding `OSPGeometricModel`s to the `clippingGeometry` array a clipping +geometry feature is enabled. Geometries assigned to this parameter +will be used as clipping geometries. Any supported geometry can be used +for clipping. The only requirement is that it has to distinctly partition +space into clipping and non-clipping one. These include: spheres, boxes, +infinite planes, closed meshes, closed subdivisions and curves. All +geometries and volumes assigned to `geometry` or `volume` will be clipped. +Use of clipping geometry that is not closed (or infinite) will result in +rendering artifacts. User can decide which part of space is clipped by +changing shading normals orientation with the `invertNormals` flag of +the [GeometricModel]. When more than single clipping geometry is defined +all clipping areas will be "added" together – an union of these areas +will be applied. + + -------------------- ---------------- ---------- -------------------------------------- + Type Name Default Description + -------------------- ---------------- ---------- -------------------------------------- + OSPGeometricModel[] geometry NULL [data] array of [GeometricModels] + + OSPVolumetricModel[] volume NULL [data] array of [VolumetricModels] + + OSPGeometricModel[] clippingGeometry NULL [data] array of [GeometricModels] + used for clipping + + bool dynamicScene false use RTC_SCENE_DYNAMIC flag (faster + BVH build, slower ray traversal), + otherwise uses RTC_SCENE_STATIC flag + (faster ray traversal, slightly + slower BVH build) + + bool compactMode false tell Embree to use a more compact BVH + in memory by trading ray traversal + performance + + bool robustMode false tell Embree to enable more robust ray + intersection code paths (slightly + slower) + -------------------- ---------------- ---------- --------------------------------------- : Parameters understood by groups. Note that groups only need to re re-committed if a geometry or volume @@ -1324,6 +1425,15 @@ world has been committed. To get this information, call OSPBounds ospGetBounds(OSPObject); +The result is returned in the provided `OSPBounds`^[`OSPBounds` has +essentially the same layout as the `OSP_BOX3F` [`OSPDataType`][data].] +struct: + + typedef struct { + float lower[3]; + float upper[3]; + } OSPBounds; + This call can also take `OSPGroup` and `OSPInstance` as well: all other object types will return an empty bounding box. @@ -1380,9 +1490,10 @@ General parameters of all renderers are float varianceThreshold 0 threshold for adaptive accumulation float / backgroundColor black, background color and alpha (RGBA), if no - vec3f / vec4f transparent map_backplate is set + vec3f / vec4f transparent `map_backplate` is set OSPTexture map_backplate optional [texture] image used as background + (use texture type `texture2d`) OSPTexture map_maxDepth optional screen-sized float [texture] with maximum far distance per pixel @@ -1448,19 +1559,20 @@ realistic materials. This renderer is created by passing the type string parameters](#renderer) understood by all renderers the path tracer supports the following special parameters: - ---------- ---------------- -------- ------------------------------------- - Type Name Default Description - ---------- ---------------- -------- ------------------------------------- - bool geometryLights true whether to render light emitted from - geometries + ---------- ------------------ -------- ------------------------------------ + Type Name Default Description + ---------- ------------------ -------- ------------------------------------ + bool geometryLights true whether geometries with an emissive + material (e.g., [Luminous]) illuminate + the scene - int roulettePathLength 5 ray recursion depth at which to - start Russian roulette termination + int roulettePathLength 5 ray recursion depth at which to + start Russian roulette termination - float maxContribution ∞ samples are clamped to this value - before they are accumulated into - the framebuffer - ---------- ---------------- -------- ------------------------------------- + float maxContribution ∞ samples are clamped to this value + before they are accumulated into + the framebuffer + ---------- ------------------ -------- ------------------------------------ : Special parameters understood by the path tracer. The path tracer requires that [materials] are assigned to [geometries], @@ -1529,18 +1641,18 @@ Note that currently only the path tracer implements colored transparency with `Tf`. Normal mapping can simulate small geometric features via the texture -`map_Bump`. The normals $n$ in the normal map are with respect to the local -tangential shading coordinate system and are encoded as $½(n+1)$, thus a -texel $(0.5, 0.5, 1)$^[respectively $(127, 127, 255)$ for 8\ bit -textures] represents the unperturbed shading normal $(0, 0, 1)$. Because -of this encoding an sRGB gamma [texture] format is ignored and normals -are always fetched as linear from a normal map. Note that the -orientation of normal maps is important for a visually consistent look: -by convention OSPRay uses a coordinate system with the origin in the -lower left corner; thus a convexity will look green toward the top of -the texture image (see also the example image of a normal map). If this -is not the case flip the normal map vertically or invert its green -channel. +`map_Bump`. The normals $n$ in the normal map are with respect to the +local tangential shading coordinate system and are encoded as $½(n+1)$, +thus a texel $(0.5, 0.5, 1)$^[respectively $(127, 127, 255)$ for 8\ bit +textures and $(32767, 32767, 65535)$ for 16\ bit textures] represents +the unperturbed shading normal $(0, 0, 1)$. Because of this encoding an +sRGB gamma [texture] format is ignored and normals are always fetched as +linear from a normal map. Note that the orientation of normal maps is +important for a visually consistent look: by convention OSPRay uses a +coordinate system with the origin in the lower left corner; thus a +convexity will look green toward the top of the texture image (see also +the example image of a normal map). If this is not the case flip the +normal map vertically or invert its green channel. ![Normal map representing an exalted square pyramidal frustum.][imgNormalMap] @@ -1873,10 +1985,11 @@ thus individual flakes are not visible. The [path tracer] supports the Luminous material which emits light uniformly in all directions and which can thus be used to turn any -geometric object into a light source. It is created by passing the type -string "`luminous`" to `ospNewMaterial`. The amount of constant -radiance that is emitted is determined by combining the general -parameters of lights: [`color` and `intensity`](#lights). +geometric object into a light source^[If `geometryLights` is enabled in +the [path tracer].]. It is created by passing the type string +"`luminous`" to `ospNewMaterial`. The amount of constant radiance that +is emitted is determined by combining the general parameters of lights: +[`color` and `intensity`](#lights). Type Name Default Description ------ ------------ -------- --------------------------------------- @@ -1921,11 +2034,15 @@ The supported texture formats for `texture2d` are: OSP_TEXTURE_RGB8 8\ bit [0–255] linear components red, green, blue OSP_TEXTURE_SRGB 8\ bit sRGB gamma encoded components red, green, blue OSP_TEXTURE_RGB32F 32\ bit float components red, green, blue - OSP_TEXTURE_R8 8\ bit [0–255] linear single component - OSP_TEXTURE_RA8 8\ bit [0–255] linear two component - OSP_TEXTURE_L8 8\ bit [0–255] gamma encoded luminance + OSP_TEXTURE_R8 8\ bit [0–255] linear single component red + OSP_TEXTURE_RA8 8\ bit [0–255] linear two components red, alpha + OSP_TEXTURE_L8 8\ bit [0–255] gamma encoded luminance (replicated into red, green, blue) OSP_TEXTURE_LA8 8\ bit [0–255] gamma encoded luminance, and linear alpha - OSP_TEXTURE_R32F 32\ bit float single component + OSP_TEXTURE_R32F 32\ bit float single component red + OSP_TEXTURE_RGBA16 16\ bit [0–65535] linear components red, green, blue, alpha + OSP_TEXTURE_RGB16 16\ bit [0–65535] linear components red, green, blue + OSP_TEXTURE_RA16 16\ bit [0–65535] linear two components red, alpha + OSP_TEXTURE_R16 16\ bit [0–65535] linear single component red ------------------- ---------------------------------------------------------- : Supported texture formats by `texture2d`, i.e., valid constants of type `OSPTextureFormat`. @@ -1949,10 +2066,10 @@ transfer function) on arbitrary surfaces inside the volume (as opposed to an isosurface showing a particular value in the volume). Its parameters are as follows - Type Name Description - --------- ------------ ------------------------------------------- - OSPVolume volume volume used to generate color lookups - --------- ------------ ------------------------------------------- + Type Name Description + ------------------ ------- ------------------------------------------- + OSPVolumetricModel volume [VolumetricModel] used to generate color lookups + ------------------ ------- ------------------------------------------- : Parameters of `volume` texture type. TextureVolume can be used for implementing slicing of volumes with any @@ -2037,10 +2154,16 @@ supports the special parameters listed in the table below. bool architectural vertical edges are projected to be parallel - int stereoMode 0: no stereo (default), - 1: left eye, - 2: right eye, - 3: side-by-side + int stereoMode `OSPStereoMode` for stereo rendering, + possible values are: + + `OSP_STEREO_NONE` (default) + + `OSP_STEREO_LEFT` + + `OSP_STEREO_RIGHT` + + `OSP_STEREO_SIDE_BY_SIDE` float interpupillaryDistance distance between left and right eye when stereo is enabled @@ -2060,7 +2183,8 @@ image. If finer control of the lens shift is needed use `imageStart` & `imageEnd`. Because the camera is now effectively leveled its image plane and thus the plane of focus is oriented parallel to the front of buildings, the whole façade appears sharp, as can be seen in the example -images below. +images below. The resolution of the [framebuffer] is not altered by +`imageStart`/`imageEnd`. ![Example image created with the perspective camera, featuring depth of field.][imgCameraPerspective] @@ -2112,11 +2236,11 @@ To get the world-space position of the geometry (if any) seen at [0–1] normalized screen-space pixel coordinates `screenPos` use void ospPick(OSPPickResult *, - OSPFrameBuffer, - OSPRenderer, - OSPCamera, - OSPWorld, - osp_vec2f screenPos); + OSPFrameBuffer, + OSPRenderer, + OSPCamera, + OSPWorld, + osp_vec2f screenPos); The result is returned in the provided `OSPPickResult` struct: @@ -2140,9 +2264,9 @@ The framebuffer holds the rendered 2D image (and optionally auxiliary information associated with pixels). To create a new framebuffer object of given size `size` (in pixels), color format, and channels use - OSPFrameBuffer ospNewFrameBuffer(osp_vec2i size, - OSPFrameBufferFormat format = OSP_FB_SRGBA, - uint32_t frameBufferChannels = OSP_FB_COLOR); + OSPFrameBuffer ospNewFrameBuffer(int size_x, int size_y, + OSPFrameBufferFormat format = OSP_FB_SRGBA, + uint32_t frameBufferChannels = OSP_FB_COLOR); The parameter `format` describes the format the color buffer has _on the host_, and the format that `ospMapFrameBuffer` will eventually return. @@ -2197,8 +2321,7 @@ using an `OSPImageOperation` [image operation]. The application can map the given channel of a framebuffer – and thus access the stored pixel information – via - const void *ospMapFrameBuffer(OSPFrameBuffer, - OSPFrameBufferChannel = OSP_FB_COLOR); + const void *ospMapFrameBuffer(OSPFrameBuffer, OSPFrameBufferChannel = OSP_FB_COLOR); Note that `OSP_FB_ACCUM` or `OSP_FB_VARIANCE` cannot be mapped. The origin of the screen coordinate system in OSPRay is the lower left @@ -2225,7 +2348,9 @@ accumulated frame can be queried with float ospGetVariance(OSPFrameBuffer); Note this value is only updated after synchronizing with `OSP_FRAME_FINISHED`, -as further described in [asynchronous rendering]. +as further described in [asynchronous rendering]. The estimated variance can +be used by the application as a quality indicator and thus to decide whether +to stop or to continue progressive rendering. The framebuffer takes a list of pixel operations to be applied to the image in sequence as an `OSPData`. The pixel operations will be run in the order @@ -2313,12 +2438,7 @@ combining a frame buffer, renderer, camera, and world. What to render and how to render it depends on the renderer's parameters. If the framebuffer supports accumulation (i.e., it was created with `OSP_FB_ACCUM`) then successive calls to `ospRenderFrame` -will progressively refine the rendered image. If additionally the -framebuffer has an `OSP_FB_VARIANCE` channel then `ospRenderFrame` -returns an estimate of the current variance of the rendered image, -otherwise `inf` is returned. The estimated variance can be used by the -application as a quality indicator and thus to decide whether to stop or -to continue progressive rendering. +will progressively refine the rendered image. To start an render task, use @@ -2375,6 +2495,16 @@ As the given running task runs (as tracked by the `OSPFuture`), applications can query a boolean [0,1] result if the passed event has been completed. +Applications can query how long an async task ran with + + float ospGetTaskDuration(OSPFuture); + +This returns the wall clock execution time of the task in seconds. If the task +is still running, this will block until the task is completed. 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 diff --git a/doc/compilation.md b/doc/compilation.md index a9e799b2b1..58cba16bca 100644 --- a/doc/compilation.md +++ b/doc/compilation.md @@ -45,7 +45,7 @@ before you can build OSPRay you need the following prerequisites: 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 0.8.0 or newer is required. If Open VKL is not + installing version 0.9.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 that adds support for Intel @@ -92,6 +92,7 @@ Run with: mkdir build cd build cmake [/scripts/superbuild] + cmake --build . On Windows make sure to select the non-default 64bit generator, e.g. @@ -117,10 +118,10 @@ 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. +: set to OFF will download a pre-built version of Open Image Denoise. BUILD_OIDN_VERSION -: determines which verison of OpenImageDenoise to pull down. +: determines which version of Open Image Denoise to pull down. For the full set of options, run: diff --git a/doc/examples.md b/doc/examples.md index ecfb552629..8df946e881 100644 --- a/doc/examples.md +++ b/doc/examples.md @@ -71,8 +71,8 @@ objects for the specific scene like `cpp::Geometry`, `cpp::Volume`, `cpp::Light` etc. The `detail::Builder` base struct is mostly responsible for setting up -OSPRay `world` and objects common in all scenes (for eg: lighting and -ground plane), which can be conveniently overridden in the derived +OSPRay `world` and objects common in all scenes (for example lighting +and ground plane), which can be conveniently overridden in the derived builders. Given below are different scenes listed with their string identifiers: @@ -117,6 +117,12 @@ unstructured_volume ### Renderer This app comes with three [renderer] options: `scivis`, `pathtracer` and -`debug`. The app provides some common rendering controls like `pixel -samples` and other more specific to the renderer type like `aoIntensity` -for `scivis` renderer. +`debug`. The app provides some common rendering controls like +`pixelSamples` and other more specific to the renderer type like +`aoIntensity` for `scivis` renderer. + +The sun-sky lighting can be used in a sample scene by enabling the +`renderSunSky` option of the `pathtracer` renderer. It allows the user +to change `turbidity` and `sunDirection`. + +![Rendering an evening sky with the `renderSunSky` option.][renderSunSky] diff --git a/doc/filter-sectionnumbers.py b/doc/filter-sectionnumbers.py new file mode 100644 index 0000000000..e9f6fc8145 --- /dev/null +++ b/doc/filter-sectionnumbers.py @@ -0,0 +1,12 @@ +# remove section numbers for subheadings +# Based on Wagner Macedo's filter.py posted at +# https://groups.google.com/forum/#!msg/pandoc-discuss/RUC-tuu_qf0/h-H3RRVt1coJ +import pandocfilters as pf + +def do_filter(k, v, f, m): + if k == "Header" and v[0] > 2: + v[1][1].append('unnumbered') + return [pf.Header(v[0], v[1], v[2])] + +if __name__ == "__main__": + pf.toJSONFilter(do_filter) diff --git a/doc/images.md b/doc/images.md index affc8ecc4b..cd0a8afede 100644 --- a/doc/images.md +++ b/doc/images.md @@ -1,7 +1,9 @@ [imgTutorial1]: tutorial_firstframe.png [imgTutorial2]: tutorial_accumulatedframe.png +[renderSunSky]: renderSunSky.png { width=90% } [ospExamples]: ospExamples.png { width=90% } [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/links.md b/doc/links.md index 2f77d7156e..ce915e37a0 100644 --- a/doc/links.md +++ b/doc/links.md @@ -11,6 +11,7 @@ [world]: documentation.html#world [data]: documentation.html#data [GeometricModel]: documentation.html#geometricmodels +[VolumetricModel]: documentation.html#volumetricmodels [texture transformations]: documentation.html#texture2d-transformations [renderer]: documentation.html#renderers [SciVis renderer]: documentation.html#scivis-renderer diff --git a/doc/ospray-doc b/doc/ospray-doc index 65d2f84417..60323a166b 160000 --- a/doc/ospray-doc +++ b/doc/ospray-doc @@ -1 +1 @@ -Subproject commit 65d2f84417a9c8ae74bf95fec5d08113f4384753 +Subproject commit 60323a166bcce0a9b298608209b8b94deaaa329c diff --git a/doc/scripts/bootstrap.min.js b/doc/scripts/bootstrap.min.js deleted file mode 100644 index c4c0d1f95c..0000000000 --- a/doc/scripts/bootstrap.min.js +++ /dev/null @@ -1,7 +0,0 @@ -/*! - * Bootstrap v4.3.1 (https://getbootstrap.com/) - * Copyright 2011-2019 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - */ -!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("jquery"),require("popper.js")):"function"==typeof define&&define.amd?define(["exports","jquery","popper.js"],e):e((t=t||self).bootstrap={},t.jQuery,t.Popper)}(this,function(t,g,u){"use strict";function i(t,e){for(var n=0;nthis._items.length-1||t<0))if(this._isSliding)g(this._element).one(Q.SLID,function(){return e.to(t)});else{if(n===t)return this.pause(),void this.cycle();var i=ndocument.documentElement.clientHeight;!this._isBodyOverflowing&&t&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!t&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},t._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},t._checkScrollbar=function(){var t=document.body.getBoundingClientRect();this._isBodyOverflowing=t.left+t.right
',trigger:"hover focus",title:"",delay:0,html:!1,selector:!1,placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent",sanitize:!0,sanitizeFn:null,whiteList:Ee},je="show",He="out",Re={HIDE:"hide"+De,HIDDEN:"hidden"+De,SHOW:"show"+De,SHOWN:"shown"+De,INSERTED:"inserted"+De,CLICK:"click"+De,FOCUSIN:"focusin"+De,FOCUSOUT:"focusout"+De,MOUSEENTER:"mouseenter"+De,MOUSELEAVE:"mouseleave"+De},xe="fade",Fe="show",Ue=".tooltip-inner",We=".arrow",qe="hover",Me="focus",Ke="click",Qe="manual",Be=function(){function i(t,e){if("undefined"==typeof u)throw new TypeError("Bootstrap's tooltips require Popper.js (https://popper.js.org/)");this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this.element=t,this.config=this._getConfig(e),this.tip=null,this._setListeners()}var t=i.prototype;return t.enable=function(){this._isEnabled=!0},t.disable=function(){this._isEnabled=!1},t.toggleEnabled=function(){this._isEnabled=!this._isEnabled},t.toggle=function(t){if(this._isEnabled)if(t){var e=this.constructor.DATA_KEY,n=g(t.currentTarget).data(e);n||(n=new this.constructor(t.currentTarget,this._getDelegateConfig()),g(t.currentTarget).data(e,n)),n._activeTrigger.click=!n._activeTrigger.click,n._isWithActiveTrigger()?n._enter(null,n):n._leave(null,n)}else{if(g(this.getTipElement()).hasClass(Fe))return void this._leave(null,this);this._enter(null,this)}},t.dispose=function(){clearTimeout(this._timeout),g.removeData(this.element,this.constructor.DATA_KEY),g(this.element).off(this.constructor.EVENT_KEY),g(this.element).closest(".modal").off("hide.bs.modal"),this.tip&&g(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,(this._activeTrigger=null)!==this._popper&&this._popper.destroy(),this._popper=null,this.element=null,this.config=null,this.tip=null},t.show=function(){var e=this;if("none"===g(this.element).css("display"))throw new Error("Please use show on visible elements");var t=g.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){g(this.element).trigger(t);var n=_.findShadowRoot(this.element),i=g.contains(null!==n?n:this.element.ownerDocument.documentElement,this.element);if(t.isDefaultPrevented()||!i)return;var o=this.getTipElement(),r=_.getUID(this.constructor.NAME);o.setAttribute("id",r),this.element.setAttribute("aria-describedby",r),this.setContent(),this.config.animation&&g(o).addClass(xe);var s="function"==typeof this.config.placement?this.config.placement.call(this,o,this.element):this.config.placement,a=this._getAttachment(s);this.addAttachmentClass(a);var l=this._getContainer();g(o).data(this.constructor.DATA_KEY,this),g.contains(this.element.ownerDocument.documentElement,this.tip)||g(o).appendTo(l),g(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new u(this.element,o,{placement:a,modifiers:{offset:this._getOffset(),flip:{behavior:this.config.fallbackPlacement},arrow:{element:We},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(t){t.originalPlacement!==t.placement&&e._handlePopperPlacementChange(t)},onUpdate:function(t){return e._handlePopperPlacementChange(t)}}),g(o).addClass(Fe),"ontouchstart"in document.documentElement&&g(document.body).children().on("mouseover",null,g.noop);var c=function(){e.config.animation&&e._fixTransition();var t=e._hoverState;e._hoverState=null,g(e.element).trigger(e.constructor.Event.SHOWN),t===He&&e._leave(null,e)};if(g(this.tip).hasClass(xe)){var h=_.getTransitionDurationFromElement(this.tip);g(this.tip).one(_.TRANSITION_END,c).emulateTransitionEnd(h)}else c()}},t.hide=function(t){var e=this,n=this.getTipElement(),i=g.Event(this.constructor.Event.HIDE),o=function(){e._hoverState!==je&&n.parentNode&&n.parentNode.removeChild(n),e._cleanTipClass(),e.element.removeAttribute("aria-describedby"),g(e.element).trigger(e.constructor.Event.HIDDEN),null!==e._popper&&e._popper.destroy(),t&&t()};if(g(this.element).trigger(i),!i.isDefaultPrevented()){if(g(n).removeClass(Fe),"ontouchstart"in document.documentElement&&g(document.body).children().off("mouseover",null,g.noop),this._activeTrigger[Ke]=!1,this._activeTrigger[Me]=!1,this._activeTrigger[qe]=!1,g(this.tip).hasClass(xe)){var r=_.getTransitionDurationFromElement(n);g(n).one(_.TRANSITION_END,o).emulateTransitionEnd(r)}else o();this._hoverState=""}},t.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},t.isWithContent=function(){return Boolean(this.getTitle())},t.addAttachmentClass=function(t){g(this.getTipElement()).addClass(Ae+"-"+t)},t.getTipElement=function(){return this.tip=this.tip||g(this.config.template)[0],this.tip},t.setContent=function(){var t=this.getTipElement();this.setElementContent(g(t.querySelectorAll(Ue)),this.getTitle()),g(t).removeClass(xe+" "+Fe)},t.setElementContent=function(t,e){"object"!=typeof e||!e.nodeType&&!e.jquery?this.config.html?(this.config.sanitize&&(e=Se(e,this.config.whiteList,this.config.sanitizeFn)),t.html(e)):t.text(e):this.config.html?g(e).parent().is(t)||t.empty().append(e):t.text(g(e).text())},t.getTitle=function(){var t=this.element.getAttribute("data-original-title");return t||(t="function"==typeof this.config.title?this.config.title.call(this.element):this.config.title),t},t._getOffset=function(){var e=this,t={};return"function"==typeof this.config.offset?t.fn=function(t){return t.offsets=l({},t.offsets,e.config.offset(t.offsets,e.element)||{}),t}:t.offset=this.config.offset,t},t._getContainer=function(){return!1===this.config.container?document.body:_.isElement(this.config.container)?g(this.config.container):g(document).find(this.config.container)},t._getAttachment=function(t){return Pe[t.toUpperCase()]},t._setListeners=function(){var i=this;this.config.trigger.split(" ").forEach(function(t){if("click"===t)g(i.element).on(i.constructor.Event.CLICK,i.config.selector,function(t){return i.toggle(t)});else if(t!==Qe){var e=t===qe?i.constructor.Event.MOUSEENTER:i.constructor.Event.FOCUSIN,n=t===qe?i.constructor.Event.MOUSELEAVE:i.constructor.Event.FOCUSOUT;g(i.element).on(e,i.config.selector,function(t){return i._enter(t)}).on(n,i.config.selector,function(t){return i._leave(t)})}}),g(this.element).closest(".modal").on("hide.bs.modal",function(){i.element&&i.hide()}),this.config.selector?this.config=l({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},t._fixTitle=function(){var t=typeof this.element.getAttribute("data-original-title");(this.element.getAttribute("title")||"string"!==t)&&(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},t._enter=function(t,e){var n=this.constructor.DATA_KEY;(e=e||g(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),g(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusin"===t.type?Me:qe]=!0),g(e.getTipElement()).hasClass(Fe)||e._hoverState===je?e._hoverState=je:(clearTimeout(e._timeout),e._hoverState=je,e.config.delay&&e.config.delay.show?e._timeout=setTimeout(function(){e._hoverState===je&&e.show()},e.config.delay.show):e.show())},t._leave=function(t,e){var n=this.constructor.DATA_KEY;(e=e||g(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),g(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusout"===t.type?Me:qe]=!1),e._isWithActiveTrigger()||(clearTimeout(e._timeout),e._hoverState=He,e.config.delay&&e.config.delay.hide?e._timeout=setTimeout(function(){e._hoverState===He&&e.hide()},e.config.delay.hide):e.hide())},t._isWithActiveTrigger=function(){for(var t in this._activeTrigger)if(this._activeTrigger[t])return!0;return!1},t._getConfig=function(t){var e=g(this.element).data();return Object.keys(e).forEach(function(t){-1!==Oe.indexOf(t)&&delete e[t]}),"number"==typeof(t=l({},this.constructor.Default,e,"object"==typeof t&&t?t:{})).delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),_.typeCheckConfig(be,t,this.constructor.DefaultType),t.sanitize&&(t.template=Se(t.template,t.whiteList,t.sanitizeFn)),t},t._getDelegateConfig=function(){var t={};if(this.config)for(var e in this.config)this.constructor.Default[e]!==this.config[e]&&(t[e]=this.config[e]);return t},t._cleanTipClass=function(){var t=g(this.getTipElement()),e=t.attr("class").match(Ne);null!==e&&e.length&&t.removeClass(e.join(""))},t._handlePopperPlacementChange=function(t){var e=t.instance;this.tip=e.popper,this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(t.placement))},t._fixTransition=function(){var t=this.getTipElement(),e=this.config.animation;null===t.getAttribute("x-placement")&&(g(t).removeClass(xe),this.config.animation=!1,this.hide(),this.show(),this.config.animation=e)},i._jQueryInterface=function(n){return this.each(function(){var t=g(this).data(Ie),e="object"==typeof n&&n;if((t||!/dispose|hide/.test(n))&&(t||(t=new i(this,e),g(this).data(Ie,t)),"string"==typeof n)){if("undefined"==typeof t[n])throw new TypeError('No method named "'+n+'"');t[n]()}})},s(i,null,[{key:"VERSION",get:function(){return"4.3.1"}},{key:"Default",get:function(){return Le}},{key:"NAME",get:function(){return be}},{key:"DATA_KEY",get:function(){return Ie}},{key:"Event",get:function(){return Re}},{key:"EVENT_KEY",get:function(){return De}},{key:"DefaultType",get:function(){return ke}}]),i}();g.fn[be]=Be._jQueryInterface,g.fn[be].Constructor=Be,g.fn[be].noConflict=function(){return g.fn[be]=we,Be._jQueryInterface};var Ve="popover",Ye="bs.popover",ze="."+Ye,Xe=g.fn[Ve],$e="bs-popover",Ge=new RegExp("(^|\\s)"+$e+"\\S+","g"),Je=l({},Be.Default,{placement:"right",trigger:"click",content:"",template:''}),Ze=l({},Be.DefaultType,{content:"(string|element|function)"}),tn="fade",en="show",nn=".popover-header",on=".popover-body",rn={HIDE:"hide"+ze,HIDDEN:"hidden"+ze,SHOW:"show"+ze,SHOWN:"shown"+ze,INSERTED:"inserted"+ze,CLICK:"click"+ze,FOCUSIN:"focusin"+ze,FOCUSOUT:"focusout"+ze,MOUSEENTER:"mouseenter"+ze,MOUSELEAVE:"mouseleave"+ze},sn=function(t){var e,n;function i(){return t.apply(this,arguments)||this}n=t,(e=i).prototype=Object.create(n.prototype),(e.prototype.constructor=e).__proto__=n;var o=i.prototype;return o.isWithContent=function(){return this.getTitle()||this._getContent()},o.addAttachmentClass=function(t){g(this.getTipElement()).addClass($e+"-"+t)},o.getTipElement=function(){return this.tip=this.tip||g(this.config.template)[0],this.tip},o.setContent=function(){var t=g(this.getTipElement());this.setElementContent(t.find(nn),this.getTitle());var e=this._getContent();"function"==typeof e&&(e=e.call(this.element)),this.setElementContent(t.find(on),e),t.removeClass(tn+" "+en)},o._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},o._cleanTipClass=function(){var t=g(this.getTipElement()),e=t.attr("class").match(Ge);null!==e&&0=this._offsets[o]&&("undefined"==typeof this._offsets[o+1]||t=0&&n0&&t-1 in e)}var E=function(e){var t,n,r,i,o,a,s,u,l,c,f,p,d,h,g,y,v,m,x,b="sizzle"+1*new Date,w=e.document,T=0,C=0,E=ae(),k=ae(),S=ae(),D=function(e,t){return e===t&&(f=!0),0},N={}.hasOwnProperty,A=[],j=A.pop,q=A.push,L=A.push,H=A.slice,O=function(e,t){for(var n=0,r=e.length;n+~]|"+M+")"+M+"*"),z=new RegExp("="+M+"*([^\\]'\"]*?)"+M+"*\\]","g"),X=new RegExp(W),U=new RegExp("^"+R+"$"),V={ID:new RegExp("^#("+R+")"),CLASS:new RegExp("^\\.("+R+")"),TAG:new RegExp("^("+R+"|[*])"),ATTR:new RegExp("^"+I),PSEUDO:new RegExp("^"+W),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+P+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},G=/^(?:input|select|textarea|button)$/i,Y=/^h\d$/i,Q=/^[^{]+\{\s*\[native \w/,J=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,K=/[+~]/,Z=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),ee=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},te=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ne=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},re=function(){p()},ie=me(function(e){return!0===e.disabled&&("form"in e||"label"in e)},{dir:"parentNode",next:"legend"});try{L.apply(A=H.call(w.childNodes),w.childNodes),A[w.childNodes.length].nodeType}catch(e){L={apply:A.length?function(e,t){q.apply(e,H.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function oe(e,t,r,i){var o,s,l,c,f,h,v,m=t&&t.ownerDocument,T=t?t.nodeType:9;if(r=r||[],"string"!=typeof e||!e||1!==T&&9!==T&&11!==T)return r;if(!i&&((t?t.ownerDocument||t:w)!==d&&p(t),t=t||d,g)){if(11!==T&&(f=J.exec(e)))if(o=f[1]){if(9===T){if(!(l=t.getElementById(o)))return r;if(l.id===o)return r.push(l),r}else if(m&&(l=m.getElementById(o))&&x(t,l)&&l.id===o)return r.push(l),r}else{if(f[2])return L.apply(r,t.getElementsByTagName(e)),r;if((o=f[3])&&n.getElementsByClassName&&t.getElementsByClassName)return L.apply(r,t.getElementsByClassName(o)),r}if(n.qsa&&!S[e+" "]&&(!y||!y.test(e))){if(1!==T)m=t,v=e;else if("object"!==t.nodeName.toLowerCase()){(c=t.getAttribute("id"))?c=c.replace(te,ne):t.setAttribute("id",c=b),s=(h=a(e)).length;while(s--)h[s]="#"+c+" "+ve(h[s]);v=h.join(","),m=K.test(e)&&ge(t.parentNode)||t}if(v)try{return L.apply(r,m.querySelectorAll(v)),r}catch(e){}finally{c===b&&t.removeAttribute("id")}}}return u(e.replace(B,"$1"),t,r,i)}function ae(){var e=[];function t(n,i){return e.push(n+" ")>r.cacheLength&&delete t[e.shift()],t[n+" "]=i}return t}function se(e){return e[b]=!0,e}function ue(e){var t=d.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function le(e,t){var n=e.split("|"),i=n.length;while(i--)r.attrHandle[n[i]]=t}function ce(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function fe(e){return function(t){return"input"===t.nodeName.toLowerCase()&&t.type===e}}function pe(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function de(e){return function(t){return"form"in t?t.parentNode&&!1===t.disabled?"label"in t?"label"in t.parentNode?t.parentNode.disabled===e:t.disabled===e:t.isDisabled===e||t.isDisabled!==!e&&ie(t)===e:t.disabled===e:"label"in t&&t.disabled===e}}function he(e){return se(function(t){return t=+t,se(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}function ge(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}n=oe.support={},o=oe.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},p=oe.setDocument=function(e){var t,i,a=e?e.ownerDocument||e:w;return a!==d&&9===a.nodeType&&a.documentElement?(d=a,h=d.documentElement,g=!o(d),w!==d&&(i=d.defaultView)&&i.top!==i&&(i.addEventListener?i.addEventListener("unload",re,!1):i.attachEvent&&i.attachEvent("onunload",re)),n.attributes=ue(function(e){return e.className="i",!e.getAttribute("className")}),n.getElementsByTagName=ue(function(e){return e.appendChild(d.createComment("")),!e.getElementsByTagName("*").length}),n.getElementsByClassName=Q.test(d.getElementsByClassName),n.getById=ue(function(e){return h.appendChild(e).id=b,!d.getElementsByName||!d.getElementsByName(b).length}),n.getById?(r.filter.ID=function(e){var t=e.replace(Z,ee);return function(e){return e.getAttribute("id")===t}},r.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&g){var n=t.getElementById(e);return n?[n]:[]}}):(r.filter.ID=function(e){var t=e.replace(Z,ee);return function(e){var n="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}},r.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&g){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),r.find.TAG=n.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):n.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},r.find.CLASS=n.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&g)return t.getElementsByClassName(e)},v=[],y=[],(n.qsa=Q.test(d.querySelectorAll))&&(ue(function(e){h.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&y.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||y.push("\\["+M+"*(?:value|"+P+")"),e.querySelectorAll("[id~="+b+"-]").length||y.push("~="),e.querySelectorAll(":checked").length||y.push(":checked"),e.querySelectorAll("a#"+b+"+*").length||y.push(".#.+[+~]")}),ue(function(e){e.innerHTML="";var t=d.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&y.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&y.push(":enabled",":disabled"),h.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&y.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),y.push(",.*:")})),(n.matchesSelector=Q.test(m=h.matches||h.webkitMatchesSelector||h.mozMatchesSelector||h.oMatchesSelector||h.msMatchesSelector))&&ue(function(e){n.disconnectedMatch=m.call(e,"*"),m.call(e,"[s!='']:x"),v.push("!=",W)}),y=y.length&&new RegExp(y.join("|")),v=v.length&&new RegExp(v.join("|")),t=Q.test(h.compareDocumentPosition),x=t||Q.test(h.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return f=!0,0;var r=!e.compareDocumentPosition-!t.compareDocumentPosition;return r||(1&(r=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!n.sortDetached&&t.compareDocumentPosition(e)===r?e===d||e.ownerDocument===w&&x(w,e)?-1:t===d||t.ownerDocument===w&&x(w,t)?1:c?O(c,e)-O(c,t):0:4&r?-1:1)}:function(e,t){if(e===t)return f=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e===d?-1:t===d?1:i?-1:o?1:c?O(c,e)-O(c,t):0;if(i===o)return ce(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?ce(a[r],s[r]):a[r]===w?-1:s[r]===w?1:0},d):d},oe.matches=function(e,t){return oe(e,null,null,t)},oe.matchesSelector=function(e,t){if((e.ownerDocument||e)!==d&&p(e),t=t.replace(z,"='$1']"),n.matchesSelector&&g&&!S[t+" "]&&(!v||!v.test(t))&&(!y||!y.test(t)))try{var r=m.call(e,t);if(r||n.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(e){}return oe(t,d,null,[e]).length>0},oe.contains=function(e,t){return(e.ownerDocument||e)!==d&&p(e),x(e,t)},oe.attr=function(e,t){(e.ownerDocument||e)!==d&&p(e);var i=r.attrHandle[t.toLowerCase()],o=i&&N.call(r.attrHandle,t.toLowerCase())?i(e,t,!g):void 0;return void 0!==o?o:n.attributes||!g?e.getAttribute(t):(o=e.getAttributeNode(t))&&o.specified?o.value:null},oe.escape=function(e){return(e+"").replace(te,ne)},oe.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},oe.uniqueSort=function(e){var t,r=[],i=0,o=0;if(f=!n.detectDuplicates,c=!n.sortStable&&e.slice(0),e.sort(D),f){while(t=e[o++])t===e[o]&&(i=r.push(o));while(i--)e.splice(r[i],1)}return c=null,e},i=oe.getText=function(e){var t,n="",r=0,o=e.nodeType;if(o){if(1===o||9===o||11===o){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=i(e)}else if(3===o||4===o)return e.nodeValue}else while(t=e[r++])n+=i(t);return n},(r=oe.selectors={cacheLength:50,createPseudo:se,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(Z,ee),e[3]=(e[3]||e[4]||e[5]||"").replace(Z,ee),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||oe.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&oe.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return V.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=a(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(Z,ee).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=E[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&E(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=oe.attr(r,e);return null==i?"!="===t:!t||(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i.replace($," ")+" ").indexOf(n)>-1:"|="===t&&(i===n||i.slice(0,n.length+1)===n+"-"))}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,f,p,d,h,g=o!==a?"nextSibling":"previousSibling",y=t.parentNode,v=s&&t.nodeName.toLowerCase(),m=!u&&!s,x=!1;if(y){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===v:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?y.firstChild:y.lastChild],a&&m){x=(d=(l=(c=(f=(p=y)[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]||[])[0]===T&&l[1])&&l[2],p=d&&y.childNodes[d];while(p=++d&&p&&p[g]||(x=d=0)||h.pop())if(1===p.nodeType&&++x&&p===t){c[e]=[T,d,x];break}}else if(m&&(x=d=(l=(c=(f=(p=t)[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]||[])[0]===T&&l[1]),!1===x)while(p=++d&&p&&p[g]||(x=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===v:1===p.nodeType)&&++x&&(m&&((c=(f=p[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]=[T,x]),p===t))break;return(x-=i)===r||x%r==0&&x/r>=0}}},PSEUDO:function(e,t){var n,i=r.pseudos[e]||r.setFilters[e.toLowerCase()]||oe.error("unsupported pseudo: "+e);return i[b]?i(t):i.length>1?(n=[e,e,"",t],r.setFilters.hasOwnProperty(e.toLowerCase())?se(function(e,n){var r,o=i(e,t),a=o.length;while(a--)e[r=O(e,o[a])]=!(n[r]=o[a])}):function(e){return i(e,0,n)}):i}},pseudos:{not:se(function(e){var t=[],n=[],r=s(e.replace(B,"$1"));return r[b]?se(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),t[0]=null,!n.pop()}}),has:se(function(e){return function(t){return oe(e,t).length>0}}),contains:se(function(e){return e=e.replace(Z,ee),function(t){return(t.textContent||t.innerText||i(t)).indexOf(e)>-1}}),lang:se(function(e){return U.test(e||"")||oe.error("unsupported lang: "+e),e=e.replace(Z,ee).toLowerCase(),function(t){var n;do{if(n=g?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return(n=n.toLowerCase())===e||0===n.indexOf(e+"-")}while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===h},focus:function(e){return e===d.activeElement&&(!d.hasFocus||d.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:de(!1),disabled:de(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!r.pseudos.empty(e)},header:function(e){return Y.test(e.nodeName)},input:function(e){return G.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:he(function(){return[0]}),last:he(function(e,t){return[t-1]}),eq:he(function(e,t,n){return[n<0?n+t:n]}),even:he(function(e,t){for(var n=0;n=0;)e.push(r);return e}),gt:he(function(e,t,n){for(var r=n<0?n+t:n;++r1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function be(e,t,n){for(var r=0,i=t.length;r-1&&(o[l]=!(a[l]=f))}}else v=we(v===a?v.splice(h,v.length):v),i?i(null,a,v,u):L.apply(a,v)})}function Ce(e){for(var t,n,i,o=e.length,a=r.relative[e[0].type],s=a||r.relative[" "],u=a?1:0,c=me(function(e){return e===t},s,!0),f=me(function(e){return O(t,e)>-1},s,!0),p=[function(e,n,r){var i=!a&&(r||n!==l)||((t=n).nodeType?c(e,n,r):f(e,n,r));return t=null,i}];u1&&xe(p),u>1&&ve(e.slice(0,u-1).concat({value:" "===e[u-2].type?"*":""})).replace(B,"$1"),n,u0,i=e.length>0,o=function(o,a,s,u,c){var f,h,y,v=0,m="0",x=o&&[],b=[],w=l,C=o||i&&r.find.TAG("*",c),E=T+=null==w?1:Math.random()||.1,k=C.length;for(c&&(l=a===d||a||c);m!==k&&null!=(f=C[m]);m++){if(i&&f){h=0,a||f.ownerDocument===d||(p(f),s=!g);while(y=e[h++])if(y(f,a||d,s)){u.push(f);break}c&&(T=E)}n&&((f=!y&&f)&&v--,o&&x.push(f))}if(v+=m,n&&m!==v){h=0;while(y=t[h++])y(x,b,a,s);if(o){if(v>0)while(m--)x[m]||b[m]||(b[m]=j.call(u));b=we(b)}L.apply(u,b),c&&!o&&b.length>0&&v+t.length>1&&oe.uniqueSort(u)}return c&&(T=E,l=w),x};return n?se(o):o}return s=oe.compile=function(e,t){var n,r=[],i=[],o=S[e+" "];if(!o){t||(t=a(e)),n=t.length;while(n--)(o=Ce(t[n]))[b]?r.push(o):i.push(o);(o=S(e,Ee(i,r))).selector=e}return o},u=oe.select=function(e,t,n,i){var o,u,l,c,f,p="function"==typeof e&&e,d=!i&&a(e=p.selector||e);if(n=n||[],1===d.length){if((u=d[0]=d[0].slice(0)).length>2&&"ID"===(l=u[0]).type&&9===t.nodeType&&g&&r.relative[u[1].type]){if(!(t=(r.find.ID(l.matches[0].replace(Z,ee),t)||[])[0]))return n;p&&(t=t.parentNode),e=e.slice(u.shift().value.length)}o=V.needsContext.test(e)?0:u.length;while(o--){if(l=u[o],r.relative[c=l.type])break;if((f=r.find[c])&&(i=f(l.matches[0].replace(Z,ee),K.test(u[0].type)&&ge(t.parentNode)||t))){if(u.splice(o,1),!(e=i.length&&ve(u)))return L.apply(n,i),n;break}}}return(p||s(e,d))(i,t,!g,n,!t||K.test(e)&&ge(t.parentNode)||t),n},n.sortStable=b.split("").sort(D).join("")===b,n.detectDuplicates=!!f,p(),n.sortDetached=ue(function(e){return 1&e.compareDocumentPosition(d.createElement("fieldset"))}),ue(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||le("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),n.attributes&&ue(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||le("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),ue(function(e){return null==e.getAttribute("disabled")})||le(P,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),oe}(e);w.find=E,w.expr=E.selectors,w.expr[":"]=w.expr.pseudos,w.uniqueSort=w.unique=E.uniqueSort,w.text=E.getText,w.isXMLDoc=E.isXML,w.contains=E.contains,w.escapeSelector=E.escape;var k=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&w(e).is(n))break;r.push(e)}return r},S=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},D=w.expr.match.needsContext;function N(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var A=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,t,n){return g(t)?w.grep(e,function(e,r){return!!t.call(e,r,e)!==n}):t.nodeType?w.grep(e,function(e){return e===t!==n}):"string"!=typeof t?w.grep(e,function(e){return u.call(t,e)>-1!==n}):w.filter(t,e,n)}w.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?w.find.matchesSelector(r,e)?[r]:[]:w.find.matches(e,w.grep(t,function(e){return 1===e.nodeType}))},w.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(w(e).filter(function(){for(t=0;t1?w.uniqueSort(n):n},filter:function(e){return this.pushStack(j(this,e||[],!1))},not:function(e){return this.pushStack(j(this,e||[],!0))},is:function(e){return!!j(this,"string"==typeof e&&D.test(e)?w(e):e||[],!1).length}});var q,L=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(w.fn.init=function(e,t,n){var i,o;if(!e)return this;if(n=n||q,"string"==typeof e){if(!(i="<"===e[0]&&">"===e[e.length-1]&&e.length>=3?[null,e,null]:L.exec(e))||!i[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(i[1]){if(t=t instanceof w?t[0]:t,w.merge(this,w.parseHTML(i[1],t&&t.nodeType?t.ownerDocument||t:r,!0)),A.test(i[1])&&w.isPlainObject(t))for(i in t)g(this[i])?this[i](t[i]):this.attr(i,t[i]);return this}return(o=r.getElementById(i[2]))&&(this[0]=o,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):g(e)?void 0!==n.ready?n.ready(e):e(w):w.makeArray(e,this)}).prototype=w.fn,q=w(r);var H=/^(?:parents|prev(?:Until|All))/,O={children:!0,contents:!0,next:!0,prev:!0};w.fn.extend({has:function(e){var t=w(e,this),n=t.length;return this.filter(function(){for(var e=0;e-1:1===n.nodeType&&w.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(o.length>1?w.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?u.call(w(e),this[0]):u.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(w.uniqueSort(w.merge(this.get(),w(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function P(e,t){while((e=e[t])&&1!==e.nodeType);return e}w.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return k(e,"parentNode")},parentsUntil:function(e,t,n){return k(e,"parentNode",n)},next:function(e){return P(e,"nextSibling")},prev:function(e){return P(e,"previousSibling")},nextAll:function(e){return k(e,"nextSibling")},prevAll:function(e){return k(e,"previousSibling")},nextUntil:function(e,t,n){return k(e,"nextSibling",n)},prevUntil:function(e,t,n){return k(e,"previousSibling",n)},siblings:function(e){return S((e.parentNode||{}).firstChild,e)},children:function(e){return S(e.firstChild)},contents:function(e){return N(e,"iframe")?e.contentDocument:(N(e,"template")&&(e=e.content||e),w.merge([],e.childNodes))}},function(e,t){w.fn[e]=function(n,r){var i=w.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=w.filter(r,i)),this.length>1&&(O[e]||w.uniqueSort(i),H.test(e)&&i.reverse()),this.pushStack(i)}});var M=/[^\x20\t\r\n\f]+/g;function R(e){var t={};return w.each(e.match(M)||[],function(e,n){t[n]=!0}),t}w.Callbacks=function(e){e="string"==typeof e?R(e):w.extend({},e);var t,n,r,i,o=[],a=[],s=-1,u=function(){for(i=i||e.once,r=t=!0;a.length;s=-1){n=a.shift();while(++s-1)o.splice(n,1),n<=s&&s--}),this},has:function(e){return e?w.inArray(e,o)>-1:o.length>0},empty:function(){return o&&(o=[]),this},disable:function(){return i=a=[],o=n="",this},disabled:function(){return!o},lock:function(){return i=a=[],n||t||(o=n=""),this},locked:function(){return!!i},fireWith:function(e,n){return i||(n=[e,(n=n||[]).slice?n.slice():n],a.push(n),t||u()),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!r}};return l};function I(e){return e}function W(e){throw e}function $(e,t,n,r){var i;try{e&&g(i=e.promise)?i.call(e).done(t).fail(n):e&&g(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}w.extend({Deferred:function(t){var n=[["notify","progress",w.Callbacks("memory"),w.Callbacks("memory"),2],["resolve","done",w.Callbacks("once memory"),w.Callbacks("once memory"),0,"resolved"],["reject","fail",w.Callbacks("once memory"),w.Callbacks("once memory"),1,"rejected"]],r="pending",i={state:function(){return r},always:function(){return o.done(arguments).fail(arguments),this},"catch":function(e){return i.then(null,e)},pipe:function(){var e=arguments;return w.Deferred(function(t){w.each(n,function(n,r){var i=g(e[r[4]])&&e[r[4]];o[r[1]](function(){var e=i&&i.apply(this,arguments);e&&g(e.promise)?e.promise().progress(t.notify).done(t.resolve).fail(t.reject):t[r[0]+"With"](this,i?[e]:arguments)})}),e=null}).promise()},then:function(t,r,i){var o=0;function a(t,n,r,i){return function(){var s=this,u=arguments,l=function(){var e,l;if(!(t=o&&(r!==W&&(s=void 0,u=[e]),n.rejectWith(s,u))}};t?c():(w.Deferred.getStackHook&&(c.stackTrace=w.Deferred.getStackHook()),e.setTimeout(c))}}return w.Deferred(function(e){n[0][3].add(a(0,e,g(i)?i:I,e.notifyWith)),n[1][3].add(a(0,e,g(t)?t:I)),n[2][3].add(a(0,e,g(r)?r:W))}).promise()},promise:function(e){return null!=e?w.extend(e,i):i}},o={};return w.each(n,function(e,t){var a=t[2],s=t[5];i[t[1]]=a.add,s&&a.add(function(){r=s},n[3-e][2].disable,n[3-e][3].disable,n[0][2].lock,n[0][3].lock),a.add(t[3].fire),o[t[0]]=function(){return o[t[0]+"With"](this===o?void 0:this,arguments),this},o[t[0]+"With"]=a.fireWith}),i.promise(o),t&&t.call(o,o),o},when:function(e){var t=arguments.length,n=t,r=Array(n),i=o.call(arguments),a=w.Deferred(),s=function(e){return function(n){r[e]=this,i[e]=arguments.length>1?o.call(arguments):n,--t||a.resolveWith(r,i)}};if(t<=1&&($(e,a.done(s(n)).resolve,a.reject,!t),"pending"===a.state()||g(i[n]&&i[n].then)))return a.then();while(n--)$(i[n],s(n),a.reject);return a.promise()}});var B=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;w.Deferred.exceptionHook=function(t,n){e.console&&e.console.warn&&t&&B.test(t.name)&&e.console.warn("jQuery.Deferred exception: "+t.message,t.stack,n)},w.readyException=function(t){e.setTimeout(function(){throw t})};var F=w.Deferred();w.fn.ready=function(e){return F.then(e)["catch"](function(e){w.readyException(e)}),this},w.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--w.readyWait:w.isReady)||(w.isReady=!0,!0!==e&&--w.readyWait>0||F.resolveWith(r,[w]))}}),w.ready.then=F.then;function _(){r.removeEventListener("DOMContentLoaded",_),e.removeEventListener("load",_),w.ready()}"complete"===r.readyState||"loading"!==r.readyState&&!r.documentElement.doScroll?e.setTimeout(w.ready):(r.addEventListener("DOMContentLoaded",_),e.addEventListener("load",_));var z=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if("object"===x(n)){i=!0;for(s in n)z(e,t,s,n[s],!0,o,a)}else if(void 0!==r&&(i=!0,g(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(w(e),n)})),t))for(;s1,null,!0)},removeData:function(e){return this.each(function(){K.remove(this,e)})}}),w.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=J.get(e,t),n&&(!r||Array.isArray(n)?r=J.access(e,t,w.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=w.queue(e,t),r=n.length,i=n.shift(),o=w._queueHooks(e,t),a=function(){w.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return J.get(e,n)||J.access(e,n,{empty:w.Callbacks("once memory").add(function(){J.remove(e,[t+"queue",n])})})}}),w.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length\x20\t\r\n\f]+)/i,he=/^$|^module$|\/(?:java|ecma)script/i,ge={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};ge.optgroup=ge.option,ge.tbody=ge.tfoot=ge.colgroup=ge.caption=ge.thead,ge.th=ge.td;function ye(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&N(e,t)?w.merge([e],n):n}function ve(e,t){for(var n=0,r=e.length;n-1)i&&i.push(o);else if(l=w.contains(o.ownerDocument,o),a=ye(f.appendChild(o),"script"),l&&ve(a),n){c=0;while(o=a[c++])he.test(o.type||"")&&n.push(o)}return f}!function(){var e=r.createDocumentFragment().appendChild(r.createElement("div")),t=r.createElement("input");t.setAttribute("type","radio"),t.setAttribute("checked","checked"),t.setAttribute("name","t"),e.appendChild(t),h.checkClone=e.cloneNode(!0).cloneNode(!0).lastChild.checked,e.innerHTML="",h.noCloneChecked=!!e.cloneNode(!0).lastChild.defaultValue}();var be=r.documentElement,we=/^key/,Te=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ce=/^([^.]*)(?:\.(.+)|)/;function Ee(){return!0}function ke(){return!1}function Se(){try{return r.activeElement}catch(e){}}function De(e,t,n,r,i,o){var a,s;if("object"==typeof t){"string"!=typeof n&&(r=r||n,n=void 0);for(s in t)De(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=ke;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return w().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=w.guid++)),e.each(function(){w.event.add(this,t,i,r,n)})}w.event={global:{},add:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,y=J.get(e);if(y){n.handler&&(n=(o=n).handler,i=o.selector),i&&w.find.matchesSelector(be,i),n.guid||(n.guid=w.guid++),(u=y.events)||(u=y.events={}),(a=y.handle)||(a=y.handle=function(t){return"undefined"!=typeof w&&w.event.triggered!==t.type?w.event.dispatch.apply(e,arguments):void 0}),l=(t=(t||"").match(M)||[""]).length;while(l--)d=g=(s=Ce.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=w.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=w.event.special[d]||{},c=w.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&w.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(e,r,h,a)||e.addEventListener&&e.addEventListener(d,a)),f.add&&(f.add.call(e,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),w.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,y=J.hasData(e)&&J.get(e);if(y&&(u=y.events)){l=(t=(t||"").match(M)||[""]).length;while(l--)if(s=Ce.exec(t[l])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){f=w.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,y.handle)||w.removeEvent(e,d,y.handle),delete u[d])}else for(d in u)w.event.remove(e,d+t[l],n,r,!0);w.isEmptyObject(u)&&J.remove(e,"handle events")}},dispatch:function(e){var t=w.event.fix(e),n,r,i,o,a,s,u=new Array(arguments.length),l=(J.get(this,"events")||{})[t.type]||[],c=w.event.special[t.type]||{};for(u[0]=t,n=1;n=1))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n-1:w.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u\x20\t\r\n\f]*)[^>]*)\/>/gi,Ae=/\s*$/g;function Le(e,t){return N(e,"table")&&N(11!==t.nodeType?t:t.firstChild,"tr")?w(e).children("tbody")[0]||e:e}function He(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Oe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Pe(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(J.hasData(e)&&(o=J.access(e),a=J.set(t,o),l=o.events)){delete a.handle,a.events={};for(i in l)for(n=0,r=l[i].length;n1&&"string"==typeof y&&!h.checkClone&&je.test(y))return e.each(function(i){var o=e.eq(i);v&&(t[0]=y.call(this,i,o.html())),Re(o,t,n,r)});if(p&&(i=xe(t,e[0].ownerDocument,!1,e,r),o=i.firstChild,1===i.childNodes.length&&(i=o),o||r)){for(u=(s=w.map(ye(i,"script"),He)).length;f")},clone:function(e,t,n){var r,i,o,a,s=e.cloneNode(!0),u=w.contains(e.ownerDocument,e);if(!(h.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||w.isXMLDoc(e)))for(a=ye(s),r=0,i=(o=ye(e)).length;r0&&ve(a,!u&&ye(e,"script")),s},cleanData:function(e){for(var t,n,r,i=w.event.special,o=0;void 0!==(n=e[o]);o++)if(Y(n)){if(t=n[J.expando]){if(t.events)for(r in t.events)i[r]?w.event.remove(n,r):w.removeEvent(n,r,t.handle);n[J.expando]=void 0}n[K.expando]&&(n[K.expando]=void 0)}}}),w.fn.extend({detach:function(e){return Ie(this,e,!0)},remove:function(e){return Ie(this,e)},text:function(e){return z(this,function(e){return void 0===e?w.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return Re(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||Le(this,e).appendChild(e)})},prepend:function(){return Re(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Le(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return Re(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return Re(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(w.cleanData(ye(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return w.clone(this,e,t)})},html:function(e){return z(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!Ae.test(e)&&!ge[(de.exec(e)||["",""])[1].toLowerCase()]){e=w.htmlPrefilter(e);try{for(;n=0&&(u+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))),u}function et(e,t,n){var r=$e(e),i=Fe(e,t,r),o="border-box"===w.css(e,"boxSizing",!1,r),a=o;if(We.test(i)){if(!n)return i;i="auto"}return a=a&&(h.boxSizingReliable()||i===e.style[t]),("auto"===i||!parseFloat(i)&&"inline"===w.css(e,"display",!1,r))&&(i=e["offset"+t[0].toUpperCase()+t.slice(1)],a=!0),(i=parseFloat(i)||0)+Ze(e,t,n||(o?"border":"content"),a,r,i)+"px"}w.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Fe(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=G(t),u=Xe.test(t),l=e.style;if(u||(t=Je(s)),a=w.cssHooks[t]||w.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];"string"==(o=typeof n)&&(i=ie.exec(n))&&i[1]&&(n=ue(e,t,i),o="number"),null!=n&&n===n&&("number"===o&&(n+=i&&i[3]||(w.cssNumber[s]?"":"px")),h.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=G(t);return Xe.test(t)||(t=Je(s)),(a=w.cssHooks[t]||w.cssHooks[s])&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=Fe(e,t,r)),"normal"===i&&t in Ve&&(i=Ve[t]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),w.each(["height","width"],function(e,t){w.cssHooks[t]={get:function(e,n,r){if(n)return!ze.test(w.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?et(e,t,r):se(e,Ue,function(){return et(e,t,r)})},set:function(e,n,r){var i,o=$e(e),a="border-box"===w.css(e,"boxSizing",!1,o),s=r&&Ze(e,t,r,a,o);return a&&h.scrollboxSize()===o.position&&(s-=Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-parseFloat(o[t])-Ze(e,t,"border",!1,o)-.5)),s&&(i=ie.exec(n))&&"px"!==(i[3]||"px")&&(e.style[t]=n,n=w.css(e,t)),Ke(e,n,s)}}}),w.cssHooks.marginLeft=_e(h.reliableMarginLeft,function(e,t){if(t)return(parseFloat(Fe(e,"marginLeft"))||e.getBoundingClientRect().left-se(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),w.each({margin:"",padding:"",border:"Width"},function(e,t){w.cssHooks[e+t]={expand:function(n){for(var r=0,i={},o="string"==typeof n?n.split(" "):[n];r<4;r++)i[e+oe[r]+t]=o[r]||o[r-2]||o[0];return i}},"margin"!==e&&(w.cssHooks[e+t].set=Ke)}),w.fn.extend({css:function(e,t){return z(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=$e(e),i=t.length;a1)}});function tt(e,t,n,r,i){return new tt.prototype.init(e,t,n,r,i)}w.Tween=tt,tt.prototype={constructor:tt,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||w.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(w.cssNumber[n]?"":"px")},cur:function(){var e=tt.propHooks[this.prop];return e&&e.get?e.get(this):tt.propHooks._default.get(this)},run:function(e){var t,n=tt.propHooks[this.prop];return this.options.duration?this.pos=t=w.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):tt.propHooks._default.set(this),this}},tt.prototype.init.prototype=tt.prototype,tt.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=w.css(e.elem,e.prop,""))&&"auto"!==t?t:0},set:function(e){w.fx.step[e.prop]?w.fx.step[e.prop](e):1!==e.elem.nodeType||null==e.elem.style[w.cssProps[e.prop]]&&!w.cssHooks[e.prop]?e.elem[e.prop]=e.now:w.style(e.elem,e.prop,e.now+e.unit)}}},tt.propHooks.scrollTop=tt.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},w.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},w.fx=tt.prototype.init,w.fx.step={};var nt,rt,it=/^(?:toggle|show|hide)$/,ot=/queueHooks$/;function at(){rt&&(!1===r.hidden&&e.requestAnimationFrame?e.requestAnimationFrame(at):e.setTimeout(at,w.fx.interval),w.fx.tick())}function st(){return e.setTimeout(function(){nt=void 0}),nt=Date.now()}function ut(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i["margin"+(n=oe[r])]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function lt(e,t,n){for(var r,i=(pt.tweeners[t]||[]).concat(pt.tweeners["*"]),o=0,a=i.length;o1)},removeAttr:function(e){return this.each(function(){w.removeAttr(this,e)})}}),w.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?w.prop(e,t,n):(1===o&&w.isXMLDoc(e)||(i=w.attrHooks[t.toLowerCase()]||(w.expr.match.bool.test(t)?dt:void 0)),void 0!==n?null===n?void w.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:null==(r=w.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!h.radioValue&&"radio"===t&&N(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(M);if(i&&1===e.nodeType)while(n=i[r++])e.removeAttribute(n)}}),dt={set:function(e,t,n){return!1===t?w.removeAttr(e,n):e.setAttribute(n,n),n}},w.each(w.expr.match.bool.source.match(/\w+/g),function(e,t){var n=ht[t]||w.find.attr;ht[t]=function(e,t,r){var i,o,a=t.toLowerCase();return r||(o=ht[a],ht[a]=i,i=null!=n(e,t,r)?a:null,ht[a]=o),i}});var gt=/^(?:input|select|textarea|button)$/i,yt=/^(?:a|area)$/i;w.fn.extend({prop:function(e,t){return z(this,w.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each(function(){delete this[w.propFix[e]||e]})}}),w.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&w.isXMLDoc(e)||(t=w.propFix[t]||t,i=w.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=w.find.attr(e,"tabindex");return t?parseInt(t,10):gt.test(e.nodeName)||yt.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),h.optSelected||(w.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),w.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){w.propFix[this.toLowerCase()]=this});function vt(e){return(e.match(M)||[]).join(" ")}function mt(e){return e.getAttribute&&e.getAttribute("class")||""}function xt(e){return Array.isArray(e)?e:"string"==typeof e?e.match(M)||[]:[]}w.fn.extend({addClass:function(e){var t,n,r,i,o,a,s,u=0;if(g(e))return this.each(function(t){w(this).addClass(e.call(this,t,mt(this)))});if((t=xt(e)).length)while(n=this[u++])if(i=mt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=t[a++])r.indexOf(" "+o+" ")<0&&(r+=o+" ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},removeClass:function(e){var t,n,r,i,o,a,s,u=0;if(g(e))return this.each(function(t){w(this).removeClass(e.call(this,t,mt(this)))});if(!arguments.length)return this.attr("class","");if((t=xt(e)).length)while(n=this[u++])if(i=mt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=t[a++])while(r.indexOf(" "+o+" ")>-1)r=r.replace(" "+o+" "," ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},toggleClass:function(e,t){var n=typeof e,r="string"===n||Array.isArray(e);return"boolean"==typeof t&&r?t?this.addClass(e):this.removeClass(e):g(e)?this.each(function(n){w(this).toggleClass(e.call(this,n,mt(this),t),t)}):this.each(function(){var t,i,o,a;if(r){i=0,o=w(this),a=xt(e);while(t=a[i++])o.hasClass(t)?o.removeClass(t):o.addClass(t)}else void 0!==e&&"boolean"!==n||((t=mt(this))&&J.set(this,"__className__",t),this.setAttribute&&this.setAttribute("class",t||!1===e?"":J.get(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;t=" "+e+" ";while(n=this[r++])if(1===n.nodeType&&(" "+vt(mt(n))+" ").indexOf(t)>-1)return!0;return!1}});var bt=/\r/g;w.fn.extend({val:function(e){var t,n,r,i=this[0];{if(arguments.length)return r=g(e),this.each(function(n){var i;1===this.nodeType&&(null==(i=r?e.call(this,n,w(this).val()):e)?i="":"number"==typeof i?i+="":Array.isArray(i)&&(i=w.map(i,function(e){return null==e?"":e+""})),(t=w.valHooks[this.type]||w.valHooks[this.nodeName.toLowerCase()])&&"set"in t&&void 0!==t.set(this,i,"value")||(this.value=i))});if(i)return(t=w.valHooks[i.type]||w.valHooks[i.nodeName.toLowerCase()])&&"get"in t&&void 0!==(n=t.get(i,"value"))?n:"string"==typeof(n=i.value)?n.replace(bt,""):null==n?"":n}}}),w.extend({valHooks:{option:{get:function(e){var t=w.find.attr(e,"value");return null!=t?t:vt(w.text(e))}},select:{get:function(e){var t,n,r,i=e.options,o=e.selectedIndex,a="select-one"===e.type,s=a?null:[],u=a?o+1:i.length;for(r=o<0?u:a?o:0;r-1)&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),w.each(["radio","checkbox"],function(){w.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=w.inArray(w(e).val(),t)>-1}},h.checkOn||(w.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),h.focusin="onfocusin"in e;var wt=/^(?:focusinfocus|focusoutblur)$/,Tt=function(e){e.stopPropagation()};w.extend(w.event,{trigger:function(t,n,i,o){var a,s,u,l,c,p,d,h,v=[i||r],m=f.call(t,"type")?t.type:t,x=f.call(t,"namespace")?t.namespace.split("."):[];if(s=h=u=i=i||r,3!==i.nodeType&&8!==i.nodeType&&!wt.test(m+w.event.triggered)&&(m.indexOf(".")>-1&&(m=(x=m.split(".")).shift(),x.sort()),c=m.indexOf(":")<0&&"on"+m,t=t[w.expando]?t:new w.Event(m,"object"==typeof t&&t),t.isTrigger=o?2:3,t.namespace=x.join("."),t.rnamespace=t.namespace?new RegExp("(^|\\.)"+x.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=void 0,t.target||(t.target=i),n=null==n?[t]:w.makeArray(n,[t]),d=w.event.special[m]||{},o||!d.trigger||!1!==d.trigger.apply(i,n))){if(!o&&!d.noBubble&&!y(i)){for(l=d.delegateType||m,wt.test(l+m)||(s=s.parentNode);s;s=s.parentNode)v.push(s),u=s;u===(i.ownerDocument||r)&&v.push(u.defaultView||u.parentWindow||e)}a=0;while((s=v[a++])&&!t.isPropagationStopped())h=s,t.type=a>1?l:d.bindType||m,(p=(J.get(s,"events")||{})[t.type]&&J.get(s,"handle"))&&p.apply(s,n),(p=c&&s[c])&&p.apply&&Y(s)&&(t.result=p.apply(s,n),!1===t.result&&t.preventDefault());return t.type=m,o||t.isDefaultPrevented()||d._default&&!1!==d._default.apply(v.pop(),n)||!Y(i)||c&&g(i[m])&&!y(i)&&((u=i[c])&&(i[c]=null),w.event.triggered=m,t.isPropagationStopped()&&h.addEventListener(m,Tt),i[m](),t.isPropagationStopped()&&h.removeEventListener(m,Tt),w.event.triggered=void 0,u&&(i[c]=u)),t.result}},simulate:function(e,t,n){var r=w.extend(new w.Event,n,{type:e,isSimulated:!0});w.event.trigger(r,null,t)}}),w.fn.extend({trigger:function(e,t){return this.each(function(){w.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return w.event.trigger(e,t,n,!0)}}),h.focusin||w.each({focus:"focusin",blur:"focusout"},function(e,t){var n=function(e){w.event.simulate(t,e.target,w.event.fix(e))};w.event.special[t]={setup:function(){var r=this.ownerDocument||this,i=J.access(r,t);i||r.addEventListener(e,n,!0),J.access(r,t,(i||0)+1)},teardown:function(){var r=this.ownerDocument||this,i=J.access(r,t)-1;i?J.access(r,t,i):(r.removeEventListener(e,n,!0),J.remove(r,t))}}});var Ct=e.location,Et=Date.now(),kt=/\?/;w.parseXML=function(t){var n;if(!t||"string"!=typeof t)return null;try{n=(new e.DOMParser).parseFromString(t,"text/xml")}catch(e){n=void 0}return n&&!n.getElementsByTagName("parsererror").length||w.error("Invalid XML: "+t),n};var St=/\[\]$/,Dt=/\r?\n/g,Nt=/^(?:submit|button|image|reset|file)$/i,At=/^(?:input|select|textarea|keygen)/i;function jt(e,t,n,r){var i;if(Array.isArray(t))w.each(t,function(t,i){n||St.test(e)?r(e,i):jt(e+"["+("object"==typeof i&&null!=i?t:"")+"]",i,n,r)});else if(n||"object"!==x(t))r(e,t);else for(i in t)jt(e+"["+i+"]",t[i],n,r)}w.param=function(e,t){var n,r=[],i=function(e,t){var n=g(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(Array.isArray(e)||e.jquery&&!w.isPlainObject(e))w.each(e,function(){i(this.name,this.value)});else for(n in e)jt(n,e[n],t,i);return r.join("&")},w.fn.extend({serialize:function(){return w.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=w.prop(this,"elements");return e?w.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!w(this).is(":disabled")&&At.test(this.nodeName)&&!Nt.test(e)&&(this.checked||!pe.test(e))}).map(function(e,t){var n=w(this).val();return null==n?null:Array.isArray(n)?w.map(n,function(e){return{name:t.name,value:e.replace(Dt,"\r\n")}}):{name:t.name,value:n.replace(Dt,"\r\n")}}).get()}});var qt=/%20/g,Lt=/#.*$/,Ht=/([?&])_=[^&]*/,Ot=/^(.*?):[ \t]*([^\r\n]*)$/gm,Pt=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Mt=/^(?:GET|HEAD)$/,Rt=/^\/\//,It={},Wt={},$t="*/".concat("*"),Bt=r.createElement("a");Bt.href=Ct.href;function Ft(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(M)||[];if(g(n))while(r=o[i++])"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function _t(e,t,n,r){var i={},o=e===Wt;function a(s){var u;return i[s]=!0,w.each(e[s]||[],function(e,s){var l=s(t,n,r);return"string"!=typeof l||o||i[l]?o?!(u=l):void 0:(t.dataTypes.unshift(l),a(l),!1)}),u}return a(t.dataTypes[0])||!i["*"]&&a("*")}function zt(e,t){var n,r,i=w.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&w.extend(!0,e,r),e}function Xt(e,t,n){var r,i,o,a,s=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}function Ut(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(!(a=l[u+" "+o]||l["* "+o]))for(i in l)if((s=i.split(" "))[1]===o&&(a=l[u+" "+s[0]]||l["* "+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(e){return{state:"parsererror",error:a?e:"No conversion from "+u+" to "+o}}}return{state:"success",data:t}}w.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Ct.href,type:"GET",isLocal:Pt.test(Ct.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":$t,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":w.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?zt(zt(e,w.ajaxSettings),t):zt(w.ajaxSettings,e)},ajaxPrefilter:Ft(It),ajaxTransport:Ft(Wt),ajax:function(t,n){"object"==typeof t&&(n=t,t=void 0),n=n||{};var i,o,a,s,u,l,c,f,p,d,h=w.ajaxSetup({},n),g=h.context||h,y=h.context&&(g.nodeType||g.jquery)?w(g):w.event,v=w.Deferred(),m=w.Callbacks("once memory"),x=h.statusCode||{},b={},T={},C="canceled",E={readyState:0,getResponseHeader:function(e){var t;if(c){if(!s){s={};while(t=Ot.exec(a))s[t[1].toLowerCase()]=t[2]}t=s[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return c?a:null},setRequestHeader:function(e,t){return null==c&&(e=T[e.toLowerCase()]=T[e.toLowerCase()]||e,b[e]=t),this},overrideMimeType:function(e){return null==c&&(h.mimeType=e),this},statusCode:function(e){var t;if(e)if(c)E.always(e[E.status]);else for(t in e)x[t]=[x[t],e[t]];return this},abort:function(e){var t=e||C;return i&&i.abort(t),k(0,t),this}};if(v.promise(E),h.url=((t||h.url||Ct.href)+"").replace(Rt,Ct.protocol+"//"),h.type=n.method||n.type||h.method||h.type,h.dataTypes=(h.dataType||"*").toLowerCase().match(M)||[""],null==h.crossDomain){l=r.createElement("a");try{l.href=h.url,l.href=l.href,h.crossDomain=Bt.protocol+"//"+Bt.host!=l.protocol+"//"+l.host}catch(e){h.crossDomain=!0}}if(h.data&&h.processData&&"string"!=typeof h.data&&(h.data=w.param(h.data,h.traditional)),_t(It,h,n,E),c)return E;(f=w.event&&h.global)&&0==w.active++&&w.event.trigger("ajaxStart"),h.type=h.type.toUpperCase(),h.hasContent=!Mt.test(h.type),o=h.url.replace(Lt,""),h.hasContent?h.data&&h.processData&&0===(h.contentType||"").indexOf("application/x-www-form-urlencoded")&&(h.data=h.data.replace(qt,"+")):(d=h.url.slice(o.length),h.data&&(h.processData||"string"==typeof h.data)&&(o+=(kt.test(o)?"&":"?")+h.data,delete h.data),!1===h.cache&&(o=o.replace(Ht,"$1"),d=(kt.test(o)?"&":"?")+"_="+Et+++d),h.url=o+d),h.ifModified&&(w.lastModified[o]&&E.setRequestHeader("If-Modified-Since",w.lastModified[o]),w.etag[o]&&E.setRequestHeader("If-None-Match",w.etag[o])),(h.data&&h.hasContent&&!1!==h.contentType||n.contentType)&&E.setRequestHeader("Content-Type",h.contentType),E.setRequestHeader("Accept",h.dataTypes[0]&&h.accepts[h.dataTypes[0]]?h.accepts[h.dataTypes[0]]+("*"!==h.dataTypes[0]?", "+$t+"; q=0.01":""):h.accepts["*"]);for(p in h.headers)E.setRequestHeader(p,h.headers[p]);if(h.beforeSend&&(!1===h.beforeSend.call(g,E,h)||c))return E.abort();if(C="abort",m.add(h.complete),E.done(h.success),E.fail(h.error),i=_t(Wt,h,n,E)){if(E.readyState=1,f&&y.trigger("ajaxSend",[E,h]),c)return E;h.async&&h.timeout>0&&(u=e.setTimeout(function(){E.abort("timeout")},h.timeout));try{c=!1,i.send(b,k)}catch(e){if(c)throw e;k(-1,e)}}else k(-1,"No Transport");function k(t,n,r,s){var l,p,d,b,T,C=n;c||(c=!0,u&&e.clearTimeout(u),i=void 0,a=s||"",E.readyState=t>0?4:0,l=t>=200&&t<300||304===t,r&&(b=Xt(h,E,r)),b=Ut(h,b,E,l),l?(h.ifModified&&((T=E.getResponseHeader("Last-Modified"))&&(w.lastModified[o]=T),(T=E.getResponseHeader("etag"))&&(w.etag[o]=T)),204===t||"HEAD"===h.type?C="nocontent":304===t?C="notmodified":(C=b.state,p=b.data,l=!(d=b.error))):(d=C,!t&&C||(C="error",t<0&&(t=0))),E.status=t,E.statusText=(n||C)+"",l?v.resolveWith(g,[p,C,E]):v.rejectWith(g,[E,C,d]),E.statusCode(x),x=void 0,f&&y.trigger(l?"ajaxSuccess":"ajaxError",[E,h,l?p:d]),m.fireWith(g,[E,C]),f&&(y.trigger("ajaxComplete",[E,h]),--w.active||w.event.trigger("ajaxStop")))}return E},getJSON:function(e,t,n){return w.get(e,t,n,"json")},getScript:function(e,t){return w.get(e,void 0,t,"script")}}),w.each(["get","post"],function(e,t){w[t]=function(e,n,r,i){return g(n)&&(i=i||r,r=n,n=void 0),w.ajax(w.extend({url:e,type:t,dataType:i,data:n,success:r},w.isPlainObject(e)&&e))}}),w._evalUrl=function(e){return w.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},w.fn.extend({wrapAll:function(e){var t;return this[0]&&(g(e)&&(e=e.call(this[0])),t=w(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(e){return g(e)?this.each(function(t){w(this).wrapInner(e.call(this,t))}):this.each(function(){var t=w(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=g(e);return this.each(function(n){w(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(e){return this.parent(e).not("body").each(function(){w(this).replaceWith(this.childNodes)}),this}}),w.expr.pseudos.hidden=function(e){return!w.expr.pseudos.visible(e)},w.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},w.ajaxSettings.xhr=function(){try{return new e.XMLHttpRequest}catch(e){}};var Vt={0:200,1223:204},Gt=w.ajaxSettings.xhr();h.cors=!!Gt&&"withCredentials"in Gt,h.ajax=Gt=!!Gt,w.ajaxTransport(function(t){var n,r;if(h.cors||Gt&&!t.crossDomain)return{send:function(i,o){var a,s=t.xhr();if(s.open(t.type,t.url,t.async,t.username,t.password),t.xhrFields)for(a in t.xhrFields)s[a]=t.xhrFields[a];t.mimeType&&s.overrideMimeType&&s.overrideMimeType(t.mimeType),t.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest");for(a in i)s.setRequestHeader(a,i[a]);n=function(e){return function(){n&&(n=r=s.onload=s.onerror=s.onabort=s.ontimeout=s.onreadystatechange=null,"abort"===e?s.abort():"error"===e?"number"!=typeof s.status?o(0,"error"):o(s.status,s.statusText):o(Vt[s.status]||s.status,s.statusText,"text"!==(s.responseType||"text")||"string"!=typeof s.responseText?{binary:s.response}:{text:s.responseText},s.getAllResponseHeaders()))}},s.onload=n(),r=s.onerror=s.ontimeout=n("error"),void 0!==s.onabort?s.onabort=r:s.onreadystatechange=function(){4===s.readyState&&e.setTimeout(function(){n&&r()})},n=n("abort");try{s.send(t.hasContent&&t.data||null)}catch(e){if(n)throw e}},abort:function(){n&&n()}}}),w.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),w.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return w.globalEval(e),e}}}),w.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),w.ajaxTransport("script",function(e){if(e.crossDomain){var t,n;return{send:function(i,o){t=w("